蓝桥杯DFS和BFS例题(学习笔记)

前言:

作为一个小白,只能通过学习宇巨的代码逐渐提高,所以在这记下来,以后多来看看,学学思路

DFS和BFS都是图的遍历的两种形式。

DFS的特点是不具有BFS中按层次顺序遍历的特性,所以DFS不具有最优性。DFS因此常用来求解有没有的问题。DFS所得到的解不一定是最优解。当题目中出现问题是否有解等字眼时,常用DFS来求解。

BFS的特点是按照层次顺序遍历,因此,BFS可以用来求解最优解,当题目中出现最短路径,最少的时间等字眼时,常用BFS来求解。

BFS:

1.迷宫问题(输出最短字典序列)

下图给出了一个迷宫的平面图,其中标记为 1 的为障碍,标记为 0 的为可 以通行的地方。迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这 个它的上、下、左、右四个方向之一。

010000 000100 001001 110000
请找出一种通过迷宫的方式,
其使用的步数最少,在步数最少的前提下,请找出字典序最小的一个作为答案。例如这个例子的输出: DRRURRDDDR
请注意在字典序中D

思路:
若用的DFS,程序运行了近 1 小时也得不出答案,最后还会爆栈,这么大的数据量早该想到的,换用 BFS,因为 BFS 得到的解总是最优解,即步数最少的解,那么我们在遍历的时候按照字典序从小到大的顺序进行四个方向遍历进行了

solve方法是bfs,用到了queue队列,其中用到了r[][]很重要,记录了每一步的方向
蓝桥杯DFS和BFS例题(学习笔记)_第1张图片

code:

#include
using namespace std;
const int MAXN=100,MAXM=100;
const int f[4]={1,0,0,-1};
const int g[4]={0,-1,1,0};
const char* s="DLRU";
char a[MAXN][MAXM];
int r[MAXN][MAXM];
typedef pair<int,int> P;
int N,M;
void printf(int x,int y)
{
	if(x!=1||y!=1){
		int t=r[x][y];
		printf(x-f[t],y-g[t]);
		cout<<s[t];
	}
 }
 void solve(){
 	queue<P> q;
 	q.push(P(1,1));
 	a[1][1]='1';
 	while(!q.empty()){
 		int x=q.front().first;
 		int y=q.front().second;
 		q.pop();
 		for(int i=0;i<4;i++){
 			int nx=x+f[i];
 			int ny=y+g[i];
 			if(a[nx][ny]=='0'){
 				a[nx][ny]='1';
 				q.push(P(nx,ny));
 				r[nx][ny]=i;
			 }
		 }
	 }
	 printf(N,M); 
 	
 } 
 int main() {
    //cin >> N >> M;
    N = 4, M = 6;
    memset(a, '1', sizeof(a));
    for (int i = 1; i <= N; ++i)
        scanf("%s", &a[i][1])/*, a[i][N + 1] = '1'*/;
    solve();
    return 0;
}

const char* s=“DLRU”;
加星号

2.森林游记(bfs)

暑假来了,绿纹龙很高兴。于是飘飘乎就来到了森林一日游。可是他却看到了很不和谐的一幕,一群猎人在森林里围捕小动物。森林可以看做是一个10*10的方格,如下图所示,1表示猎人,0表示小动物。

蓝桥杯DFS和BFS例题(学习笔记)_第2张图片

已知猎人保持不动,而小动物可以往上下左右任意方向逃脱(当然不能撞上猎人)。小动物可以逃出森林。但上图背景色被标红的那部分小动物将永远无法逃脱猎人的魔爪。

Input 一个10*10的矩阵,描述森林中猎人和小动物分布的情况。保证每个点要么为猎人,要么为小动物。

Output 一个整数,表示不能逃脱猎人魔爪的小动物数量。

思路:
先bfs整个矩阵,把能走出去的vis标记为1,然后最后统计vis为0的即可
bfs过程中 因为不能出去的那些都是被1围住的,像围棋,所以bfs是走不到里面的

code:

#include

using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef unsigned long long ull;
const int inf = 0x3f3f3f3f;
const int maxn = 2e5 + 7;
const ll mod = 1e9+7;

#define mst(x, a) memset( x,a,sizeof(x) )
#define rep(i, a, b) for(int i=(a);i<=(b);++i)
#define dep(i, a, b) for(int i=(a);i>=(b);--i)

ll read() {
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') {
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') {
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
void out(ll x) {
	int stackk[40];
	if (x < 0) {
		putchar('-');
		x = -x;
	}
	if (!x) {
		putchar('0');
		return;
	}
	int top = 0;
	while (x) stackk[++top] = x % 10, x /= 10;
	while (top) putchar(stackk[top--] + '0');
}

int a[12][12],n=10,vis[12][12];
int dx[4]= {1,0,-1,0};
int dy[4]= {0,1,0,-1};
void bfs() {
	queue<PII>q;
	q.push({0,0});
	vis[0][0] = 1;
	while(q.size()) {
		PII fr = q.front();
		q.pop();
		int x = fr.first;
		int y = fr.second;
		for(int i=0 ; i<4 ; i++) {
			int x1 = x+dx[i];
			int y1 = y+dy[i];
			if(vis[x1][y1]) continue; 
			if(a[x1][y1]) continue;
			if(x1<0||x1>n+1||y1<0||y1>n+1) continue;
			vis[x1][y1] =1;
			q.push({x1,y1});
		}
	}
}
int main() {
	int s=0;
//	rep(i,0,n) rep(j,0,n) a[i][j] =0;
	rep(i,1,n) rep(j,1,n) a[i][j] = read();
	bfs();
	int ans=0;
	rep(i,1,n) rep(j,1,n) if(a[i][j]==0&&vis[i][j]==0) ans++;
	out(ans);
	return 0;
}
/*

*/

3.马的遍历

题目描述
有一个n*m的棋盘(1

输入格式
一行四个数据,棋盘的大小和马的坐标

输出格式
一个n*m的矩阵,代表马到达某个点最少要走几步(左对齐,宽5格,不能到达则输出-1)

输入输出样例
输入
3 3 1 1
输出
0 3 2
3 -1 1
2 1 4

code:

#include
using namespace std;
const int maxn=1e5+10;
const int dx[8]={-1,-2,-2,-1,1,2,2,1};
const int dy[8]={2,1,-1,-2,2,1,-1,-2};//8个方向
queue<pair<int,int> > q;
int f[500][500];//存步数
bool vis[500][500]; 
int main(){
	int n,m;
	int x,y;
	cin>>n>>m>>x>>y;
	memset(f,-1,sizeof(f));
	memset(vis,false,sizeof(vis));
	f[x][y]=0;
	vis[x][y]=true;
	q.push(make_pair(x,y));
	while(!q.empty())
	{
		int xx=q.front().first;
		int yy=q.front().second;
		q.pop();
		for(int i=0;i<8;i++)
		{
			int u=xx+dx[i],v=yy+dy[i];
			if(u<1||u>n||v<1||v>m||vis[u][v])continue;//出界或走过就不走
		    vis[u][v]=true;q.push(make_pair(u,v));f[u][v]=f[xx][yy]+1;
		}
	}
	
	for(int i=1;i<=n;i++)
	 {for(int j=1;j<=m;j++)printf("%-5d",f[i][j]);printf("\n");}//注意场宽!!
	return 0;
	
	} 
   

4.奇怪的电梯
蓝桥杯DFS和BFS例题(学习笔记)_第3张图片

题目描述

呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第ii层楼(1≤i≤N)上有一个数字K i。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:3, 3 ,1
,253,3,1,2,5代表了K_i在1楼,按“上”可以到4楼,按“下”是不起作用的,因为没有-2楼。那么,从A楼到B楼至少要按几次按钮呢?

输入格式 共二行。

第一行为33个用空格隔开的正整数,表示N,A,B(1≤N≤200, 1≤A,B≤N)N,A,B(1≤N≤200,1≤A,B≤N)。

第二行为NN个用空格隔开的非负整数,表示K_iK i ​

输出格式
一行,即最少按键次数,若无法到达,则输出-1−1。

输入输出样例
输入
5 1 5
3 3 1 2 5
输出
3

code(dfs):

#include
#include
using namespace std;
int n,a,b,ans=0x7ffffff;
int to[205];
bool vis[205];
void dfs(int now,int sum)//now表示当前搜到的楼层,sum表示按钮次数
{
    if(now==b) ans=min(ans,sum);
    if(sum>ans) return;
    vis[now]=1;
    //不越界就搜
    if(now+to[now]<=n&&!vis[now+to[now]]) dfs(now+to[now],sum+1);
    if(now-to[now]>=1&&!vis[now-to[now]]) dfs(now-to[now],sum+1);
    vis[now]=0;//回溯
}
int main()
{
    scanf("%d%d%d",&n,&a,&b);
    for(int i=1;i<=n;i++) scanf("%d",&to[i]);
    vis[a]=1;
    dfs(a,0);
    if(ans!=0x7ffffff) printf("%d",ans);
    else printf("-1");
    return 0;
}

code(bfs):

#include
using namespace std;
const int maxn=1e5+10;
int n,a,b,to[205];
bool vis[205];
struct node{int id,step;}x;//id表示楼层号,step表示按钮次数
queue<node> q;

int main(){
	cin>>n>>a>>b;
	for(int i=1;i<=n;i++) cin>>to[i];
	q.push((node){a,0});
	while(q.size())
	{
		x=q.front();
		q.pop();
		if(x.id==b)
		{
			cout<<x.step<<endl;
			return 0;
		}
		if(x.id+to[x.id]>=1&&!vis[x.id+to[x.id]])
		{
			q.push((node){x.id+to[x.id],x.step+1});
			vis[x.id+to[x.id]]=1;
		}
		if(x.id-to[x.id]>=1&&!vis[x.id-to[x.id]])
		{
			q.push((node){x.id-to[x.id],x.step+1});
			vis[x.id-to[x.id]]=1;
		}
	 }
	 printf("-1");
	 return 0; 
}

DFS

1.着色问题

给定一个具有n个顶点的图。要给图上的每个顶点染色、并且要使相邻的顶点颜色不同。问是否能最多用2种颜色进行染色?题目保证没有重边和自环。

思路:
judge函数是判断以pos为起点,有无存在一条边使二维数组等于1,且相邻的这个点颜色不重复

code:

#include
using namespace std;

const int MAX=105;			//定义最大的顶点数
int n,m,num,ans;			//分别代表点数、边数、可用颜色数和最终的答案
int map[MAX][MAX];			//对于小规模的数量可以直接使用邻接矩阵(也可以用向量优化空间)
int color[MAX]; 			//color[i]=x表示第i个点涂上代号为x的颜色

bool judge(int pos,int col) //检测在pos位上涂颜色代号为col的方案是否可行
{
	for(int i=1;i<=n;i++)
		if(map[pos][i] && color[i]==col) return false;
	return true; 
}
void dfs(int pos)
{
	if(pos>n){
		ans++;
		return;
	}
	for(int i=1;i<=num;i++){
		if(judge(pos,i))	//如果可以在pos位上涂上颜色i
		{
			color[pos]=i;	//那么就给pos位涂上颜色i
			dfs(pos+1);
			color[pos]=0;	//回退时必须把刚才点的着色去掉,否则有可能会影响下次的dfs 
		}
	}
}
int main()
{
	int x,y; 
	cin>>n>>m>>num;
	for(int i=0;i<m;i++)
	{
		cin>>x>>y;
		map[x][y]=map[y][x]=1;
	}
	dfs(1);
	cout<<ans<<endl;
	return 0;
}

2.分考场

n个人参加某项特殊考试。
为了公平,要求任何两个认识的人不能分在同一个考场。
求是少需要分几个考场才能满足条件。

输入格式

第一行,一个整数n(1  以下m行每行的格式为:两个整数a,b,用空格分开 (1<=a,b<=n) 表示第a个人与第b个人认识。

输出格式   
一行一个整数,表示最少分几个考场。

思路:
先对所有教室进行遍历,每次遍历一个教室的时候,遍历此时这个人和教室里每个人的关系是否为1,如果出现认识,那就遍历下一个教室。如果教室全遍历完,就再开一间教室,记得下面为回溯做准备,设置pop_back
dfs结束条件即是安排的学生数量大于原本的人数,这一层return结束,开始回溯
code:

#include
#include
using namespace std;

const int MAX=105;
bool map[MAX][MAX];			 	//map[x][y]表示x和y认识
int n,m;
int ans=MAX;					//最开始必须给ans赋值一个最大值(为最多的人数即可)
vector<int> classroom[MAX];	 	//二维向量classroom[i]=x表示
void dfs(int order,int roomNum)	//order表示某个学生的序号,roomNum表示当前教室的数量 
{
	if(roomNum>=ans) return;	//当现在安排的教室数量已经大于了最小的教室数量的话放弃搜索并回退 
	if(order>n){				//安排的学生数量已经大于所有的学生,就表示已经安排完了所有的学生
		ans=roomNum;
		return;
	}
	for(int i=1;classroom[i].size();i++)	//遍历所有教室
	{ 
		int j,len=classroom[i].size();
		for(j=0;j<len;j++)					//检测当前教室是否存在一个人与将要被安排的学生认识 
			if(map[order][classroom[i][j]]) break;
		if(j==len)							//说明当前教室中没有人与当前order的学生认识
		{
			classroom[i].push_back(order);	//在当前教室插入此学生 
			dfs(order+1,roomNum); 	   		//继续安排下一个 
			classroom[i].pop_back();   		//回退时需要把这个学生从当前教室清除掉 
		} 
	}
	classroom[roomNum+1].push_back(order);	//开一间新教室给当前学生 
	dfs(order+1,roomNum+1);					//继续安排下一个 
	classroom[roomNum+1].pop_back();		//回退后需要把这个学生从当前教室清除掉 
}
int main()
{
	int x,y;
	cin>>n>>m;
	for(int i=0;i<m;i++)
	{
		cin>>x>>y;
		map[x][y]=map[y][x]=1;
	}
	dfs(1,0);
	cout<<ans<<endl;
	return 0;
}

3.走方格

【问题描述】
在平面上有一些二维的点阵。
这些点的编号就像二维数组的编号一样,从上到下依次为第 1 至第 n 行,
从左到右依次为第 1 至第 m 列,每一个点可以用行号和列号来表示。
现在有个人站在第 1 行第 1 列,要走到第 n 行第 m 列。只能向右或者向下
走。
注意,如果行号和列数都是偶数,不能走入这一格中。
问有多少种方案。
【输入格式】
输入一行包含两个整数 n, m。
【输出格式】
输出一个整数,表示答案。
【样例输入】
3 4
【样例输出】
2
【样例输入】
6 6
【样例输出】
0
【评测用例规模与约定】
对于所有评测用例,1 ≤ n ≤ 30, 1 ≤ m ≤ 30。
*/

code:

#include

int n,m;
int cns=0;
int dfs(int a,int b)
{
	if(a%2==0&&b%2==0) return 0;
	if(a==n&&b==m) cns++;
	if(a+1<=n) dfs(a+1,b);
	if(b+1<=m) dfs(a,b+1);
}
int main()
{
	scanf("%d%d",&n,&m);
	dfs(1,1);
    printf("%d",cns);
	return 0;	
}

4.分两半

把一个6*6正方形分成两半,一共多少种方案

思路:
沿着线进行dfs,而不是对格子
code:

#include
using namespace std;
int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};

bool vis[10][10];
int res=0;
void dfs(int x,int y){
	if(x==0||y==0||x==6||y==6){
		res++;
		return;
	}
	for(int i=1;i<=4;i++)
	{
		int tempx=x+dir[i][0];
		int tempy=y+dir[i][1];
		
		if(!vis[tempx][tempy])
		{
			vis[tempx][tempy]=1;
			vis[6-tempx][6-tempy]=1;
			dfs(tempx,tempy);
			vis[tempx][tempy] = 0;
			vis[6-tempx][6 - tempy] = 0;
		}
	}
}
int main()
{
vis[3][3] = 1;
	DFS(3,3);
	cout << ans/4 <<endl;
	return 0;	
}

5.小天狼星的访问

经过数月的准备,小天狼星,一个被诬陷的杀人犯,准备闯入霍格沃茨见见他的侄子。霍格沃茨的地图呈一颗树状分布。每个房间由若干跳过道通向其他房间。由于小天狼星想尽快找到哈利:
0.他会从房间0开始找
1.他总是会选择离自己最近的房间找
2.如果没找到,则继续选最近的房间深入
3.如果已没有房间可走,则返回到上一个房间,继续选择(往回走也算时间哦)。
4.当然,除了往回走,小天狼星是不会去一个房间两次的。

Input 第1行,n房间个数,p哈利所在的房间。(p≤n<100)
第2∼n行,每行3个整数s,t,l。从房间s到房间t的时间l。(s≠t, 0≤s

Output 输出找到哈利的时间(开始时间为0)。
Samples
Input Copy
5 2
0 1 1
0 2 2
1 3 3
1 4 4
Output
18

思路:
这个最近的房间不是根据数字大小,而是到达相连房间时间最少的那一个。找到下一个点后,标记此点搜索过,然后判断到达目标点了吗,没有的话dfs找到的这个点,如果此点后无路径,回溯至上一层,同时时间也要加上回来的时间
ps:不是所有的题都套用模板 标记走过为1 回溯变为0 像涂色问题确实是这样,因为不恢复原本 都无法找到下一个不一样的涂色方案
但像此题,比如到达4点,标记来过,回溯的时候加上了回来的时间,但是不用取消标记,因为下次访问不会再来他了,就不用再错一次了
code:

#include
#include
#define MAXN 100+10
#define oo 99999999
 
int n,p,ans=0,map[MAXN][MAXN];
bool h[MAXN];
 
void dfs(int i)
{
  int k,min,j;
  while(true)
  {
    k=0;min=oo;
    for(j=1;j<=n;j++)
      if(!h[j] && map[i][j]>0 && min>map[i][j])
        {min=map[i][j];k=j;}
    if(min==oo) return;
    h[k]=true;
    ans+=map[i][k];//从 i 到 k
    if(k==p){printf("%d",ans);exit(0);}
    dfs(k);
    ans+=map[i][k];//无路可走时从 k 回到 i
  }
}
 
int main()
{
 
  scanf("%d%d",&n,&p);
  int i,s,t,l;
  for(i=1;i<=n-1;i++)
  {
    scanf("%d%d%d",&s,&t,&l);
    map[s][t]=l;map[t][s]=l;
  }
  h[0]=true;
  dfs(0);
  printf("%d",ans);
  return 0;
}

6.活蹦乱跳的香穗子

香穗子在田野上调蘑菇!她跳啊跳,发现自己很无聊,于是她想了一个有趣的事情,每个格子最多只能经过1次,且每个格子都有其价值

跳的规则是这样的,香穗子可以向上下左右四个方向跳到相邻的格子,并且她只能往价值更高(这里是严格的大于)的格子跳.

香穗子可以从任意的格子出发,在任意的格子结束, 那么她最多能跳几次?

Input
第一行n,m, 表示田野的长和宽

接下来n行,每行m个数,表示该格的价值

Output
一个数,表示最多跳得格数

Samples
Input Copy
2 2
2 5
-1 3
Output
2

思路:
需要从每个点出发,来判断最大值是多少,所以循环dfs,每次dfs的时候做好vis标记是否走过,并且回溯取消标记。每次循环dfs前先恢复vis数组原始值

code:

#include

using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef unsigned long long ull;
const int inf = 0x3f3f3f3f;
const int maxn = 2e5 + 700;
const ll mod = 1e9+7;

#define mst(x, a) memset( x,a,sizeof(x) )
#define rep(i, a, b) for(int i=(a);i<=(b);++i)
#define dep(i, a, b) for(int i=(a);i>=(b);--i)

ll read() {
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') {
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') {
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
void out(ll x) {
	int stackk[40];
	if (x < 0) {
		putchar('-');
		x = -x;
	}
	if (!x) {
		putchar('0');
		return;
	}
	int top = 0;
	while (x) stackk[++top] = x % 10, x /= 10;
	while (top) putchar(stackk[top--] + '0');
}
ll qpow(ll a,ll b) {
	ll ans=1;
	while(b) {
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
//int n,m,a,b;
//vectorv[666];
ll n,m,a[121][122];
ll dx[4] = {1,-1,0,0};
ll dy[4]=  {0,0,1,-1};
ll vis[121][121],ans,step[121][121],vi[121][102];
void dfs(ll x,ll y,ll val) {

	ans = max(ans,val);
	for(int i=0 ; i<4 ; i++) {
		int x1 = x+dx[i];
		int y1 = y+dy[i];
		if(x1<1||x1>n||y1<1||y1>m) continue;
		if(a[x1][y1]<=a[x][y]) continue;
		if(vi[x1][y1]) continue;

		vi[x1][y1] =1;
		dfs(x1,y1,val+1);
		vi[x1][y1] =0;

	}

}
int main() {
	/*
	scanf("%d%d%d%d",&n,&m,&a,&b);
	for(int i=1 ; i<=m ; i++) {
		int x,y;
		scanf("%d%d",&x,&y);
		v[x].push_back(y);
		v[y].push_back(x);
	}
	*/
	n=read(),m=read();
	rep(i,1,n) rep(j,1,m) a[i][j]=read();
	for(int i=1 ; i<=n ; i++) {
		for(int j=1 ; j<=m ; j++) {
			//	if(vis[i][j]) continue;
			mst(vi,0);
			dfs(i,j,0);
		}
	}
	out(ans);
	return 0;
}

/*

7.P1036 [NOIP2002 普及组] 选数

题目描述:
已知 n 个整数 从n个整数中任选k个整数相加,可分别得到一系列的和。例如当n=4,k=3 4个整数分别为3,7,12,19时,可得全部的组合与它们的和为:

3+7+12=22

3+7+19=29

7+12+19=38

3+12+19=34

现在,要求你计算出和为素数共有多少种。

例如上例,只有一种的和为素数:3+7+19=29
蓝桥杯DFS和BFS例题(学习笔记)_第4张图片
思路:
例如 3 7 12 19进行深搜即可
先是第1个循环 i=0 i<4 只走了第一层 也就是sum加入了3
然后第2个循环 i=1 i<4 只走了第一层 也就是sum加入了7
然后第3个循环 i=2 i<4 只走了第一层 也就是sum加入了12
然后判读这三个数是否是素数 判断完后开始回溯
先回溯了第三个循环 开始从i=3执行 便是 3 7 19
再回溯了第二个循环 开始从i=2执行 便是 3 12
然后i=3 便是3 12 19
再回溯第一个循环

code:

#include 
#include 
using namespace std;

bool isprime(int a){//判断素数
    /*0和1特判真的没啥用对这题
    吐槽:题中n的数据范围很奇怪,
    n还有可能=1.....那k
    for(int i = 2;i * i <= a; i++)//不想用sqrt,还要头文件
        if(a % i == 0)//如果整除
            return false;//扔回false
    //程序都到这里的话就说明此为素数
    //否则就被扔回了
    return true;//扔回true
}

int n,k;
int a[25];
long long ans;

void dfs(int m, int sum, int startx){//最重要的递归
//m代表现在选择了多少个数
//sum表示当前的和
//startx表示升序排列,以免算重
    if(m == k){//如果选完了的话
        if(isprime(sum))//如果和是素数
            ans++;//ans加一
        return ;
    }
    for(int i = startx; i < n; i++)
        dfs(m + 1, sum + a[i], i + 1);//递归
        //步数要加一,和也要加
        //升序起始值要变成i+1,以免算重
    return ;//这一个步骤下,所有的都枚举完了
    //直接返回去
}

int main(){
    scanf("%d%d",&n,&k);//输入
    
    for(int i = 0; i < n; i++)
        scanf("%d",&a[i]);//循环读入
    dfs(0,0,0);//调用函数
    printf("%d\n",ans);//输出答案
    return 0;//结束程序
}

8.P2089 烤鸡
猪猪 Hanke 特别喜欢吃烤鸡(本是同畜牲,相煎何太急!)Hanke 吃鸡很特别,为什么特别呢?因为他有 10 种配料(芥末、孜然等),每种配料可以放 1 到 3 克,任意烤鸡的美味程度为所有配料质量之和。

现在, Hanke 想要知道,如果给你一个美味程度 n ,请输出这 10 种配料的所有搭配方案。
蓝桥杯DFS和BFS例题(学习笔记)_第5张图片
输入
1
输出
1
1 1 1 1 1 1 1 1 1 2
1 1 1 1 1 1 1 1 2 1
1 1 1 1 1 1 1 2 1 1
1 1 1 1 1 1 2 1 1 1
1 1 1 1 1 2 1 1 1 1
1 1 1 1 2 1 1 1 1 1
1 1 1 2 1 1 1 1 1 1
1 1 2 1 1 1 1 1 1 1
1 2 1 1 1 1 1 1 1 1
2 1 1 1 1 1 1 1 1 1
思路:
1.可以十层for循环暴力跑,这题不超时
2.dfs搜索 关键在于怎么储存这十个数,可以开个数组储存,回溯的时候记得取消
code:

#include
#include
using namespace std;
int n,ans1,ans2[10001][11],sum,a[11];
void trys(int t,int m)//t代表当前的尝试的调料。m代表当前美味程度
{
    if (t>10) 
    {
        if (m==n) //如果美味程度与猪猪的要求相等 
        {
            ans1++;//统计方案总数 
            for (int i=1;i<=10;i++)
            ans2[ans1][i]=a[i];//存入答案的数组 
        }
        return ;
    }
    for (int i=1;i<=3;i++)
    {
        if (m+i>n) break;//如果超过了要求,那么后面的就可以直接忽略 
        a[t]=i;//储存答案 
        trys(t+1,m+i);//查看下一种调料 
        a[t]=0;//状态恢复 
    }
}
int main()
{
    cin>>n;
    trys(1,0);//从第一种调料开始尝试,美味程度为0 
    cout<<ans1<<endl;
    for (int i=1;i<=ans1;i++)
    {
        for (int j=1;j<=10;j++)
            cout<<ans2[i][j]<<" ";
        cout<<endl;
    }//输出结果 
    return 0;
} 

9.敢死队(经典思路)
蓝桥杯DFS和BFS例题(学习笔记)_第6张图片
code:

#include
#include 
using namespace std;
int sol_sup[100005];
bool vis[100005];
int n;
int ans;
bool check(int idx)
{
	if(vis[sol_sup[idx]]) return false;
	return true;
 } 
 void choose(int sum,int need,int idx)
 {
 	if(sum==need)
 	{
 		ans++;
 		return;
	 }
	 for(int i=idx+1;i<=n;i++){
	 	if(check(i))
	 	{
	 		vis[i]=1;
	 		choose(sum+1,need,i);
	 		vis[i]=0;
		 }
	 }
 }
int main()
{
	//输入
	cin >> n;
	sol_sup[1] = 1;
	for (int i = 2; i <= n; i++) cin >> sol_sup[i];

	//选1~n个成员
	for (int i = 1; i <= n; i++)
	{
		memset(vis, 0, sizeof(vis));
		choose(0, i, 0);
	}

	cout << ans;
	return 0;
}

10.数独题
蓝桥杯DFS和BFS例题(学习笔记)_第7张图片
code:

#include
using namespace std;
int map[10][10];

int range(int n)
{
	if (n >= 1 && n <= 3) return 1;
	else if (n >= 4 && n <= 6) return 4;
	else return 7;
}

bool check(int x, int y, int n)
{
	//检查行列
	for (int i = 1; i <= 9; i++)
	{
		if (map[x][i] == n || map[i][y] == n) return false;
	}

	//检查九宫格
	int r = range(x);
	int c = range(y);
	for (int i = r; i <= r + 2; i++)
	{
		for (int j = c; j <= c + 2; j++)
		{
			if (map[i][j] == n) return false;
		}
	}
	return true;
}

void dfs(int x, int y)
{
	//完成填数,输出
	if (y > 9)
	{
		for (int i = 1; i <= 9; i++)
		{
			for (int j = 1; j <= 9; j++)
			{
				cout << map[i][j];
			}
			cout << endl;
		}
		exit(0);
	}

	if (map[x][y] == 0)
	{
		for (int i = 1; i <= 9; i++)
		{
			if (check(x, y, i))
			{
				map[x][y] = i;
				dfs((x + 1) % 10, y + (x + 1) / 10);
				map[x][y] = 0;
			}
		}
	}
	else dfs((x + 1) % 10, y + (x + 1) / 10);

}

int main()
{
	//输入
	for (int i = 1; i <= 9; i++)
	{
		string s;
		cin >> s;
		for (int j = 0; j < 9; j++)
		{
			if (s[j] != '0') map[i][j + 1] = s[j] - '0';
		}
	}

	dfs(1, 1);

	return 0;
}

你可能感兴趣的:(算法及数据结构,dfs,算法,bfs)