DFS(深度优先搜索)
深度优先搜索被称为暴力搜索,简单的来说它属于递归,是按照一条路走到底,再回溯到上一次递归中。搜索时一条路走到黑后,才回头。一般适用于求解迷宫方案总数,连通块问题等问题,不过搜索时耗时较大。
下面直接看题吧:
全排列问题
题目描述
按照字典序输出自然数 1 1 1 到 n n n 所有不重复的排列,即 n n n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。
输入格式
一个整数 n n n。
输出格式
由 1 ∼ n 1 \sim n 1∼n 组成的所有不重复的数字序列,每行一个序列。
每个数字保留 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 1≤n≤9。
首先题目要求按字典序输出自然数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 1≤N,M≤5, 1 ≤ T ≤ 10 1 \le T \le 10 1≤T≤10, 1 ≤ S X , F X ≤ n 1 \le SX,FX \le n 1≤SX,FX≤n, 1 ≤ S Y , F Y ≤ m 1 \le SY,FY \le m 1≤SY,FY≤m。
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 1≤i≤N)上有一个数字 K i K_i Ki( 0 ≤ K i ≤ N 0 \le K_i \le N 0≤Ki≤N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如: 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 1≤N≤200, 1 ≤ A , B ≤ N 1 \le A, B \le N 1≤A,B≤N)。
第二行为 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
1≤N≤200,
1
≤
A
,
B
≤
N
1 \le A, B \le N
1≤A,B≤N,
0
≤
K
i
≤
N
0 \le K_i \le N
0≤Ki≤N。
代码如下:
每一层所记录的次数会有不同,开个数组记录
#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(1≤n≤30)。
接下来 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 1≤n≤30。
#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;
}
以上就是全部内容了。