本文已参与「新人创作礼」活动,一起开启掘金创作之路。
蓝桥杯练习034
部分和
题目描述
给定整数序列a1,a2,…,an,判断是否可以从中选出若干数,使它们的和恰好为k.
1≤n≤20
-10^8≤ai≤10^8
-10^8≤k≤10^8
样例:
输入
4
1 2 4 7
13
输出:
Yes (13=2+4+7)
解题思路
1.通过对输入数据的取与不取来凑k
2.对每一个位置上的元素都可以取或者不取
3.可以先考虑取第一个位置的元素,此时剩余k-a[0]还需要从剩余元素中取
4.然后考虑下一个位置
5.当k小于0或者数组下标越界时要返回上一次的位置
6.当k恰好为0时说明已经找到了满足的方式,根据要求输出结果
7.需要注意的是:不选cur位置元素的时候,dfs后要将该元素加入vec中
便于下次搜索要这个元素的情况
8.最后要回溯。将加入的元素删除
代码
#include<iostream>
#include<vector>
#include<cstdlib>
using namespace std;
int kk;
void dfs(int a[],int n,int k,int cur,vector<int> v){
if(k==0){//找到满足的值
cout<<"Yes("<<kk<<"=";
int size=v.size();
for(int i=0;i<size;i++)
cout<<v[i]<<(i==size-1?"":"+");
cout<<")"<<endl;
exit(0);//退出程序
}
//k小于0或cur和n相等,没有找到
if(k<0||cur==n)return;
dfs(a,n,k,cur+1,v);//不要cur这个元素
v.push_back(a[cur]);//将cur位置的元素加入v中
dfs(a,n,k-a[cur],cur+1,v);//要cur这个元素
v.pop_back(); //回溯
}
int main()
{
int n,k;
cin>>n;
int a[n];
vector<int> vec;
for(int i=0;i<n;i++)
cin>>a[i];
cin>>k;
kk=k;//保存k的值,便于输出状态
dfs(a,n,k,0,vec);
return 0;
}
蓝桥杯练习035
水洼数目
问题描述:
有一个大小为N * M的院子,雨后积起了水,
八连通的积水被认为是连在一起的,请求出园子里面总共有多少水洼
限制条件
N, M <=100
输入
10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........W..
.........W..
..W.......W.
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.
输出
3
解题思路
(1)从头开始进行遍历,寻找第一个积水W
(1.1)从该处进行dfs遍历,
寻找其8个方向是否有积水W,如果存在将其改为陆地.(避免重复访问), 继续进行遍历。
(1.2)每完成一次dfs遍历,就遍历完了一片水洼
对水洼进行计数
注意:
(1)为了避免重复访问,每遍历完一个积水W,就将其改为陆地.
(2)访问8个方向时,可借助双层for循环
代码
#include<iostream>
#include<cstring>
using namespace std;
int N,M;
void dfs(char **map,int i,int j){
map[i][j]='.';//先将当前访问的积水W 改为 陆地.
//访问a[i][j]8个方向(分别对i和j进行-1、+1、+0)
for(int k=-1;k<2;k++){//k和l分别表示坐标i,j累加的量(横坐标)
for(int l=-1;l<2;l++){//纵坐标
if(k==0&&l==0)continue;//方向没有发生变化
if(i+k>=0&&i+k<=N-1&&j+l>=0&&j+l<=M-1){
if(map[i+k][j+l]=='W')
//注意:只有下一个位置是积水时,才进行访问
dfs(map,i+k,j+l);
}
}
}
}
int main()
{
cin>>N>>M;
char **map=new char*[N];
for(int i=0;i<M;i++)
map[i]=new char[M];
for(int i=0;i<N;i++)
for(int j=0;j<M;j++)
cin>>map[i][j];
int cnt=0;
for(int i=0;i<N;i++){
for(int j=0;j<M;j++){
if(map[i][j]=='W'){
dfs(map,i,j);//清除一个水洼
cnt++; //计数
}
}
}
cout<<cnt<<endl;
return 0;
}
蓝桥杯练习036
n皇后
题目描述
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.
Given an integer n, return all distinct solutions to the n-queens puzzle.
Return the total number of distinct solutions
n-queens谜题是将n个女王放在n×n棋盘上,这样就没有两个女王的问题
互相攻击。
给定一个整数 n,将所有不同的解返回到 n-queens 难题。
返回不同解决方案的总数
解题思路
1.可以用dfs来求解本问题
2.用全局变量cnt记录可以摆放的方案数
3.n为要求的n皇后,本例中用4皇后演示,根据需要对n进行更改
4.巧用一位数组,rec[i]=j表示i行放到j列
5.dfs退出条件为:放的行数和皇后数目相等
6.rec[i]==col||i+rec[i]==row+col||rec[i]-i==col-row
用来检测是否冲突,分别是行和列冲突,主对角线冲突,副对角线冲突
代码
#include<iostream>
using namespace std;
const int n=4;
int cnt;
int rec[n];
void dfs(int row){
if(row==n){
cnt++;
return;
}
//依次尝试在某列上放一个皇后
for(int col=0;col<n;col++){
bool ok=true;
//检验这个皇后是否和之前放置的皇后有冲突
for(int i=0;i<row;i++){
if(rec[i]==col||i+rec[i]==row+col||rec[i]-i==col-row){
ok=false;
break;
}
}
//剪枝
if(ok){
rec[row]=col;//标记
dfs(row+1);//继续找下一行
}
}
}
int main()
{
dfs(0);
cout<<cnt<<endl;
return 0;
}
素数环
题目描述
输入正整数n,对1~n进行排列,使得相邻两个数之和均为素数,
输出时从整数1开始,逆时针排列。同一个环应恰好输出一次。
n<=16
如输入:6
输出:
1 4 3 2 5 6
1 6 5 2 3 4
解题思路
1.首先想到的是进行全排列,然后筛选出满足条件的
2.全排列可能会超时,从而另寻他路
3.用dfs求解
3.1首先向数组中填入数字1
3.2然后考虑向其中填入2~n
3.3填入之前判断数组中是否已经填了,同时
判断能不能填入其中,可以填则填入,否则,
dfs深搜下一个数,回溯。
3.4封装了一个判断是否能填入的函数,
当前数组中没有该元素
当前元素与前一个元素的和为素数
代码
#include<iostream>
using namespace std;
void Print(int *rec,int len){
for(int i=0;i<len;i++)
cout<<rec[i]<<(i==n-1?"":" ");
cout<<endl;
}
bool isP(int k){
for(int i=2;i*i<=k;i++){
if(k%i==0)return false;
}
return true;
}
bool check(int *res,int i,int cur,int n){
for(int a=0;a<n;a++){
if(res[a]==i||!isP(res[cur-1]+i))
return false;
}
return true;
}
void dfs(int n,int *rec,int cur){
//填到末尾了,还有首尾相加为素数才可以
if(cur==n&&isP(rec[0]+rec[n-1])){
Print(rec,n);
return;
}
//尝试用每个数字填到cur这个位置
for(int i=2;i<=n;i++){
//rec中没有i这个数,且和上一个数之和为素数
if(check(rec,i,cur,n)){
//尝试将i放在cur位置,往前走一步
rec[cur]=i;
dfs(n,rec,cur+1);
rec[cur]=0;//回溯
}
}
}
int main()
{
int n;
cin>>n;
int *rec=new int[n];
rec[0]=1;
dfs(n,rec,1);
return 0;
}
#include<iostream>
#include<vector>
using namespace std;
void Print(vector<int> &rec) {
for (int i = 0; i < rec.size(); i++)
cout << rec[i]<<(i==rec.size()-1?"":" ") ;
cout << endl;
}
bool isP(int k) {
for (int i = 2; i * i <= k; i++) {
if (k % i == 0)return false;
}
return true;
}
bool check(vector<int> &rec, int i, int cur) {
for (int a = 0; a < rec.size(); a++) {
if (rec[a] == i || !isP(rec[cur - 1] + i))
return false;
}
return true;
}
void dfs(int n, vector<int> &rec, int cur) {
//填到末尾了,还有首尾相加为素数才可以
if (cur == n && isP(rec[0] + rec[n - 1])) {
Print(rec);
return;
}
//尝试用每个数字填到cur这个位置
for (int i = 2; i <= n; i++) {
//rec中没有i这个数,且和上一个数之和为素数
if (check(rec, i, cur)) {
//尝试将i放在cur位置,往前走一步
rec[cur]=i;
dfs(n, rec, cur + 1);
rec[cur] = 0;//回溯
}
}
}
int main()
{
int n;
cin >> n;
vector<int> rec(n,0);
rec[0]=1;
dfs(n, rec, 1);
return 0;
}
蓝桥杯练习037
硬币问题
问题描述
有1元,5元,10元,50元,100元,500元的硬币各c1,c5,c10,c100,c500枚
现在要用这些硬币来支付A元,最少需要多少枚硬币?
假定本题至少存在一种支付方案。
0<=ci<=10^9
0<=A<=10^9
输入
第一行有六个数字,分别代表从小到大6种面值的硬币的个数
第二行为A,代表需支付的A元
样例:
输入:
3 2 1 3 0 2
620
输出
6
解题思路
1.本题要求最少的硬币数目,可以采用贪心算法
2.用局部最优得到全局最优
3.从面值最大的硬币开始选,来凑需要的面额A
3.1A比最大面值小,则考虑前一个面值
3.2计算出所需要的当前面值的数量
3.3递归求解剩余的面额所需要的硬币数量
代码
#include<iostream>
using namespace std;
int cnts[6];
const int coins[6]={1,5,10,50,100,500};
/*尽量先用大面值,因为不用大面值
*将使用更多的小面值硬币
×一定得不到最优解
*/
int f(int A,int cur){
if(A<=0)return 0;
if(cur==0)return A;
int coinValue=coins[cur];
//金额有多少个coinValue
int x=A/coinValue;
//当前面值的硬币有cnt个
int cnt=cnts[cur];
int t=min(x,cnt);
//用t个当前面值,剩下的继续处理
return t+f(A-t*coinValue,cur-1);
}
int main()
{
for(int i=0;i<6;i++)
cin>>cnts[i];
int A;
cin>>A;
int res=f(A,5);
cout<<res<<endl;
return 0;
}
背包问题
问题描述
有n个重量和价值分别为wi,vi的物品,从这些物品中挑选出
总重量不超过W的物品,求所有挑选方案中价值总和的最大值。
1<=n<=100
1<=wi,vi<=100
1<=W<=10000
输入
n=4
(w,v)={(2,3),(1,2),(3,4),(2,2)}
W=5
输出
7
分析
因为对每个物品只有选和不选两种情况,
所以这个问题称为01背包。
解题思路
1.对每个物品可以选择选也可以选择不选(在重量不超过W的前提下)
2.当选中一个物体时,剩余的W要考虑剩下的物品;
不选中时,W考虑剩下物品,两种情况取最大值
3.本题可以用dfs求解,也可用备忘录方式求解
代码
特定数据
#include<iostream>
using namespace std;
const int n=4;//物品数量
const int W=5;//背包的承重极限
int w[]={2,1,3,2};//重量表
int v[]={3,2,4,2};//价值表
int rec[n][W+1];//备忘录表
int dfs(int i,int ww){
if(ww<=0)return 0;//装不进去
if(i==n)return 0;//没物品可选
//不选当前物品
int v2=dfs(i+1,ww);
if(ww>=w[i]){
//选择当前物品
int v1=v[i]+dfs(i+1,ww-w[i]);
return max(v1,v2);
}else{
return v2;
}
}
int dfs1(int i,int ww){
if(i==n)return 0;
if(ww<=0)return 0;
//计算之前先查询
if(rec[i][ww]>=0)
return rec[i][ww];
//不选择当前物品
int v2=dfs1(i+1,ww);
int ans;
if(ww>=w[i]){
//选择当期物品
int v1=v[i]+dfs1(i+1,ww-w[i]);
ans=max(v1,v2);
}else{
ans=v2;
}
// 计算之后做记录
rec[i][ww]=ans;
return ans;
}
int main()
{
int ww=W;
for(int i=0;i<n;i++){
for(int j=0;j<W+1;j++)
rec[i][j]=-1;
}
int ans=dfs(0,ww);
cout<<ans<<endl;
ww=W;
ans=dfs1(0,ww);
cout<<ans<<endl;
return 0;
}
手动输入版
#include<iostream>
using namespace std;
void GetArray(int arr[],int n){
for(int i=0;i<n;i++)
cin>>arr[i];
}
int dfs(int n,int w[],int v[],int i,int ww){
if(ww<=0)return 0;
if(i==n)return 0;
int v2=dfs(n,w,v,i+1,ww);
if(ww>=w[i]){
int v1=v[i]+dfs(n,w,v,i+1,ww-w[i]);
return max(v1,v2);
}else{
return v2;
}
}
void init(int **rec,int n,int m){
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
rec[i][j]=-1;
}
int dfs1(int n,int w[],int v[],int i,int ww,int **rec){
if(ww<=0)return 0;
if(i==n)return 0;
if(rec[i][ww]>=0){
return rec[i][ww];
}
int v2=dfs1(n,w,v,i+1,ww,rec);
int ans;
if(ww>=w[i]){
int v1=v[i]+dfs1(n,w,v,i+1,ww-w[i],rec);
ans=max(v1,v2);
}else{
ans=v2;
}
rec[i][ww]=ans;
return ans;
}
int dp(int w[],int v[],int n,int W){
int **dp=new int*[n];
for(int i=0;i<n;i++)
dp[i]=new int[W+1];
//初始化dp表的第一行
for(int i=0;i<W+1;i++){
if(i>=w[0]){//每种容量-0号物品
dp[0][i]=v[0];
}else{
dp[0][i]=0;
}
}
//其他行
for(int i=1;i<n;i++){
//j是列,也是背包的剩余容量
for(int j=0;j<W+1;j++){
if(j>=w[i]){//要得起
//选择当前物品即i号物品,剩余容量
int i1=v[i]+dp[i-1][j-w[i]];
int i2=dp[i-1][j];
dp[i][j]=max(i1,i2);
}else{
dp[i][j]=dp[i-1][j];
}
}
}
return dp[n-1][W];
}
int main()
{
int n,W;
cin>>n>>W;
int w[n];
int v[n];
GetArray(w,n);
GetArray(v,n);
int ww=W;
int ans=dfs(n,w,v,0,ww);
cout<<ans<<endl;
int **rec=new int*[n];
for(int i=0;i<n;i++)
rec[i]=new int[W+1];
init(rec,n,W+1);
ww=W;
ans=dfs1(n,w,v,0,ww,rec);
cout<<ans<<endl;
cout<<dp(w,v,n,W)<<endl;
return 0;
}