蓝桥杯练习题

255 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

蓝桥杯练习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;
}