DFS与BFS

DFS(深度优先搜索)

深度优先搜索被称为暴力搜索,简单的来说它属于递归,是按照一条路走到底,再回溯到上一次递归中。搜索时一条路走到黑后,才回头。一般适用于求解迷宫方案总数,连通块问题等问题,不过搜索时耗时较大。
下面直接看题吧:

全排列问题

题目描述

按照字典序输出自然数 1 1 1 n n n 所有不重复的排列,即 n n n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。

输入格式

一个整数 n n n

输出格式

1 ∼ n 1 \sim n 1n 组成的所有不重复的数字序列,每行一个序列。

每个数字保留 5 5 5 个场宽。

样例 #1

样例输入 #1

3

样例输出 #1

    1    2    3
    1    3    2
    2    1    3
    2    3    1
    3    1    2
    3    2    1

提示

1 ≤ n ≤ 9 1 \leq n \leq 9 1n9

首先题目要求按字典序输出自然数1到n所有不重复的排列,样例的输出结果可以看出来先从1开始排,123与132一开始的第二个数先选了2后面再选择第二个数为3。用DFS来看的话就是选择1,接着往下选择2,在往下3,选完3后就没有了,得到一种将它输出。回溯到上一层选3再选2有又一种。此时第二层找完后回溯到第一层选2,不断重复即可输出全排列。
代码如下:

#include<iostream>
#include<string.h>
#include<cstdio>
using namespace std;
int n;
int ans[15];
//用来标记是否走过
bool flag[15];

void dfs(int k)  //k代表了当前为第几层
{
	
       if(k==n+1)  //一般dfs中都要有结束的出口,因为k是从一开始,所以结束条件n要+1
       {
       //输出一次选择的结果
       for(int i=1;i<=n;i++)
       cout<<"    "<<ans[i];
        cout<<endl;
        return;
       }
       //
       for(int i=1;i<=n;i++)
       {
       if(!flag[i])
       {
        flag[i]=true;
        ans[k]=i;
        dfs(k+1);
        //递归完后,要将数字标记成未使用过.
        flag[i]=false;
       }
           
       }
}
int main()
{
	  //将flag数组设置为false
	  memset(flag,false,sizeof(flag));
      cin>>n;
      dfs(1);
      return 0;
}

第二题:

迷宫

题目描述

给定一个 N × M N \times M N×M 方格的迷宫,迷宫里有 T T T 处障碍,障碍处不可通过。

在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。

给定起点坐标和终点坐标,每个方格最多经过一次,问有多少种从起点坐标到终点坐标的方案。

输入格式

第一行为三个正整数 N , M , T N,M,T N,M,T,分别表示迷宫的长宽和障碍总数。

第二行为四个正整数 S X , S Y , F X , F Y SX,SY,FX,FY SX,SY,FX,FY S X , S Y SX,SY SX,SY 代表起点坐标, F X , F Y FX,FY FX,FY 代表终点坐标。

接下来 T T T 行,每行两个正整数,表示障碍点的坐标。

输出格式

输出从起点坐标到终点坐标的方案总数。

样例 #1

样例输入 #1

2 2 1
1 1 2 2
1 2

样例输出 #1

1

提示

对于 100 % 100\% 100% 的数据, 1 ≤ N , M ≤ 5 1 \le N,M \le 5 1N,M5 1 ≤ T ≤ 10 1 \le T \le 10 1T10 1 ≤ S X , F X ≤ n 1 \le SX,FX \le n 1SX,FXn 1 ≤ S Y , F Y ≤ m 1 \le SY,FY \le m 1SY,FYm

DFS的模板,一条路搜到底,符合就加一。
代码如下

#include<iostream>

using namespace std;
int n,m,l;
int mapp[10][10];
int x1,y1,x2,y2;
//移动方式 
int movex[4]={1,-1,0,0};
int movey[4]={0,0,1,-1};
//记录可以达到的次数 
int cnt=0;
void dfs(int sx,int sy)
{
	int x,y;
	if(sx==x2&&sy==y2)
	{
		cnt++;
		return;
	}
	else
	{
	  for(int i=0;i<4;i++)
	   {
		x=sx+movex[i];
		y=sy+movey[i];
		
		
		if(x>=1&&x<=n&&y>=1&&y<=m&&mapp[x][y]!=1)
		{
		//直接将地图走过的设置为1 
		mapp[x][y]=1;
		dfs(x,y);
		//恢复到没走过 
	    mapp[x][y]=0;
		}
	
	   }
	}
	
}
int main()
{
	cin>>n>>m>>l;
	cin>>x1>>y1>>x2>>y2;
	int x,y;
	for(int i=1;i<=l;i++)
	{
		cin>>x>>y;
		mapp[x][y]=1;
	}
	//标记初始位置已经走过了,否则就会导致结果变多 
	mapp[x1][y1]=1;
	dfs(x1,y1);
	cout<<cnt;
	return 0;
}

BFS (广度优先搜索)

属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。遍历时采取一层一层的遍历,直到搜到底为止。通常用队列来实现该算法。

奇怪的电梯

题目描述

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

输入格式

共二行。

第一行为三个用空格隔开的正整数,表示 N , A , B N, A, B N,A,B 1 ≤ N ≤ 200 1 \le N \le 200 1N200 1 ≤ A , B ≤ N 1 \le A, B \le N 1A,BN)。

第二行为 N N N 个用空格隔开的非负整数,表示 K i K_i Ki

输出格式

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

样例 #1

样例输入 #1

5 1 5
3 3 1 2 5

样例输出 #1

3

提示

对于 100 % 100 \% 100% 的数据, 1 ≤ N ≤ 200 1 \le N \le 200 1N200 1 ≤ A , B ≤ N 1 \le A, B \le N 1A,BN 0 ≤ K i ≤ N 0 \le K_i \le N 0KiN
代码如下:
每一层所记录的次数会有不同,开个数组记录

#include<iostream>
#include<queue>
using namespace std;
int n,a,b;
int ans[210];
int movey[2]={-1,1};
//标记是否走过,如果走过了就不用在去了 
int flag[210];
//非常重要,用来存储上一个到下一个所按的次数 
int cnt[210];
queue<int> q;
void bfs(int k) //表示层数 
{
	//nf为当前的层数,lf为下一步所到的层数 
	int nf,lf;
	if(k==b)
	{
		cout<<cnt[k];
		return;
	}
	else
	{
		q.push(k);
		while(!q.empty())
		{
			
			nf=q.front();
			if(nf==b)
			{
				cout<<cnt[nf];
				return;
			}
			q.pop();
			for(int i=0;i<=1;i++)
			{
				lf=nf+movey[i]*ans[nf];
				if(lf>=1&&lf<=n&&flag[lf]!=1)
				{
				   flag[lf]=1;
				   q.push(lf);
				   //上一层所按的次数加一
				   cnt[lf]=cnt[nf]+1;	
				}
				
			}
			
		}
		
		cout<<-1;
	}
	
}
int main()
{
	
	cin>>n>>a>>b;
	for(int i=1;i<=n;i++)
	cin>>ans[i];
	//初始化值 
	cnt[a]=0;
	flag[a]=1;
	bfs(a);
	
	return 0; 
}

下一个题:

填涂颜色

题目描述

由数字 0 0 0 组成的方阵中,有一任意形状闭合圈,闭合圈由数字 1 1 1 构成,围圈时只走上下左右 4 4 4 个方向。现要求把闭合圈内的所有空间都填写成 2 2 2。例如: 6 × 6 6\times 6 6×6 的方阵( n = 6 n=6 n=6),涂色前和涂色后的方阵如下:

0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1

输入格式

每组测试数据第一行一个整数 n ( 1 ≤ n ≤ 30 ) n(1 \le n \le 30) n(1n30)

接下来 n n n 行,由 0 0 0 1 1 1 组成的 n × n n \times n n×n 的方阵。

方阵内只有一个闭合圈,圈内至少有一个 0 0 0

//感谢黄小U饮品指出本题数据和数据格式不一样. 已修改(输入格式)

输出格式

已经填好数字 2 2 2 的完整方阵。

样例 #1

样例输入 #1

6
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1

样例输出 #1

0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1

提示

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 30 1 \le n \le 30 1n30

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
using namespace std;
//声明一个PII类型 
typedef pair<int, int> PII;
int n;
int ans[32][32];
int flag[32][32];
int movex[4]={1,-1,0,0};
int movey[4]={0,0,1,-1};
int sx,sy;
void dfs(int x,int y)
{
	queue<PII>q;
	
	ans[x][y]=3;
	flag[x][y]=1;
	q.push({x,y});
	while(q.size())
	{
		//这里的p是PII类型 
		PII p=q.front();
		q.pop();
		for(int i=0;i<4;i++)
		{
			
			sx=p.first+movex[i];
			sy=p.second+movey[i];
			//因为打印图形时从一开始,此时的边界为[0,n+1]. 
			if(sx>=0&&sx<=n+1&&sy>=0&&sy<=n+1&&ans[sx][sy]==0&&flag[sx][sy]==0)
			{
				ans[sx][sy]=3;
				flag[sx][sy]=1;
				q.push({sx,sy});
				
			}
		}
		
	}
	
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++) 
		{
		cin>>ans[i][j];
		}
	}
				
    dfs(0,0);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(ans[i][j]==3) ans[i][j]=0;
			else if(ans[i][j]==0) ans[i][j]=2;
		}
	}
	
	for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cout<<ans[i][j]<<" ";
        }
        cout<<endl;
    }
	
	return 0;
}

以上就是全部内容了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值