深度优先搜索习题整理,持续更新…
文章目录
1. 中国象棋
题目描述
中国象棋博大精深,其中马的规则最为复杂,也是最难操控的一颗棋子。我们都知道象棋中马走"日",比如在 (2,4)(2, 4)(2,4) 位置的一个马,跳一步能到达的位置有 (0,3)(0, 3)(0,3),(0,5)(0, 5)(0,5),(1,2)(1, 2)(1,2),(1,6)(1, 6)(1,6),(3,2)(3, 2)(3,2),(3,6)(3, 6)(3,6),(4,3)(4, 3)(4,3),(4,5)(4, 5)(4,5)。
蒜头君正在和花椰妹下棋,蒜头君正在进行战略布局,他需要把在 (x,y)(x,y)(x,y) 位置的马跳到 (x′,y′)(x’, y’)(x′,y′) 位置,以达到威慑的目的。但是棋盘大小有限制,棋盘是一个 10×9 的网格,左上角坐标为 (0,0)(0, 0)(0,0),右下角坐标为 (9,8)(9, 8)(9,8),马不能走出棋盘,并且有些地方已经有了棋子,马也不能跳到有棋子的点。蒜头君想知道,在不移动其他棋子的情况下,能否完成他的战略目标。
输入格式
输入一共 101010 行,每行一个长度为 999 的字符串。
输入表示这个棋盘,我们用’.‘表示空位置,用’#'表示该位置有棋子,
用’S’表示初始的马的位置,用’T’表示马需要跳到的位置。
输入保证一定只存在一个’S’和一个’T’。
输出格式
如果在不移动其他棋子的情况下,马能从’S’跳到’T’,样例输入 那么输出一行"Yes",否则输出一行"No"。
样例输入
.#....#S#
..#.#.#..
..##.#..#
......##.
...T.....
...#.#...
...#.....
...###...
.........
.##......
样例输出
Yes
代码
#include<iostream>
using namespace std;
char maze[15][15];//地图
int dir[8][2]={{-2,-1},{-1,-2},{1,-2},{2,-1},{2,1},{1,2},{-1,2},{-2,1}};//方向
bool vis[15][15];//标记是否走过
bool in(int x,int y){
return 0<=x&&x<10&&0<=y&&y<9;
}
bool dfs(int x,int y){
if(maze[x][y]=='T'){
return true;
}
vis[x][y]=true;
for(int i=0;i<8;i++){
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(maze[tx][ty]!='#' && in(tx,ty) && !vis[tx][ty]){
if(dfs(tx,ty)){
return true;
}
}
}
// vis[x][y]=false;这里我们如果不取消标记反而可以提高代码的运行效率。
//因为我们这里只需要判断两个点是否可达,到达过的状态就没有必要再次到达了。
return false;
}
int main(){
for(int i=0;i<10;i++){
cin>>maze[i];
}
int x,y;
for(int i=0;i<10;i++){
for(int j=0;j<9;j++){
if(maze[i][j]=='S'){
x=i;
y=j;
}
}
}
// dfs(x,y)
if(dfs(x,y)){
cout<<"Yes"<<endl;
}else{
cout<<"No"<<endl;
}
return 0;
}
2. 踏青
题目描述
样例输入
5 6
. # . . . .
. . # . . .
. . # . . #
. . . # # .
. # . . . .
样例输出
5
代码
3. 迷宫解的方案数
题目描述:
在不走重复路径的情况下,总共有多少不同可以到达终点的路径呢,
Input
第一行输入两个整数 n(1 ≤ n ≤ 11), m(1 ≤ m ≤ 11).表示迷宫的行和列。
然后有一个 n × m 的地图,地图由’.’、’#’、‘s’、‘e’这四个部分组成。
’.‘表示可以通行的路,’#'表示迷宫的墙,'s’表示起始点,'e’表示终点。
Output
输出一个整数,表示从’s’到达’e’的所有方案数。
Sample Input 1
5 5
s####
.####
.####
.####
….e
Sample Output 1
1
Sample Input 2
3 3
s…
…#
…e
Sample Output 2
7
代码:
#include <stdlib.h>
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;
int start_x,start_y,end_x,end_y;
int n,m;
char map[105][105];//存储坐标系
int vis[105][105];//存储该点是否被访问过
int ans;//方案的个数
bool in(int x,int y)
{
return x>=0&&x<n&&y>=0&&y<m;
}
void dfs(int x,int y)
{
if(!in(x,y)||vis[x][y]||map[x][y]=='#')
return ;
if(x==end_x&&y==end_y)
{
ans++; //统计方案数!!!!
return ;
}
vis[x][y]=1;
dfs(x,y+1);
dfs(x+1,y);
dfs(x,y-1);
dfs(x-1,y);
vis[x][y]=0;//注意这里,记得取消标记 !!!!
}
int main()//'#'代表故宫的墙 ,'.'则表示可以通行,'s'代表起点,'e'代表终点
{
int i,j;
cin>>n>>m;
for(i=0;i<n;i++)
for(j=0;j<m;j++)
cin>>map[i][j];
for(i=0;i<n;i++)//找到起点和终点的纵坐标
{
for(j=0;j<m;j++)
{
if(map[i][j]=='s')
{
start_x=i;
start_y=j;
}
if(map[i][j]=='e')
{
end_x=i;
end_y=j;
}
}
}
dfs(start_x,start_y);//搜索的起点为s的横纵坐标
cout<<ans<<endl;
return 0;
}
4. 最大的蛋糕块
样例输入:
. # . . . .
. . . # . .
. . . # . .
. . # . . .
. . # . . .
样例输出:
2
代码:
char maze[105][105];
bool vis[15][105];
int n, m;
int cnt = 0;
void dfs(int x, int y) {
if (x < 0 || x >= n || y < 0 || y >= m || vis[x][y] || maze[x][y] == '.') {
return;
}
vis[x][y] = true;
cnt++;
dfs(x - 1, y);
dfs(x + 1, y);
dfs(x, y - 1);
dfs(x, y + 1);
}
int main() {
int ans = -1;
cin >> n >> m;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> maze[i][j];
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (!vis[i][j] && maze[i][j] == '#') {
cnt = 0; //每次运行dfs之前必须将cnt置为0
dfs(i, j);
if (cnt > ans) {
ans = cnt;
}
}
}
}
cout << ans << endl;
return 0;
}
5. 马的覆盖点
题目描述:
代码:
char a[105][105];
int n,m,x,y;
int direction[8][2] = {{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1}};
void dfs(int x,int y,int step)
{
if(step > 3)
return;
else
a[x][y] = '#'; //能够到达就标记 #
for(int i = 0;i < 8;i++){
int dx = x + direction[i][0];
int dy = y + direction[i][1];
if(dx >= 1 && dx <= n && dy >= 1 && dy <= m){
dfs(dx,dy,step + 1);
}
}
}
int main()
{
cin >> n >> m >> x >> y;
for(int i = 1;i <= n;i++)
for(int j = 1;j <= m;j++)
a[i][j] = '.';
a[x][y] = '#';
dfs(x,y,0); //若i,j从0开始遍历则dfs(x-1,y-1,0)
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++)
cout << a[i][j];
cout << endl;
}
return 0;
}
6. 蒜头君开公司
题目描述:
2020 年,蒜头君自己开了一家拥有 N个员工的大公司。每天,蒜头君都要分配 N项工作给他的员工,但是,由于能力的不同,每个人对处理相同工作所需要的时间有快有慢。众所周知,
蒜头君是一个非常重视效率的人,他想知道该如何分配工作,才能使得完成所有工作的时间总和最小。
(每个员工只可以被分配到一个工作)。但是我们也都知道蒜头君不是一般的懒,
所以蒜头君找到了你,请你拯救一下蒜头君吧!
输入格式
第一行输入一个整数 N,代表有 N个员工,员工编号从 1到 N。(1≤N≤10)
接着输入一个 NN的二维矩阵 task[N][N],
task[i][j] 指的是第 i项工作如果由 j号员工完成所需要的时间。(0≤tas**k*[i][j]≤1000)
输出格式
输出结果包括一个整数,代表所需要的最少时间(求和)。
样例输入
6
10 11 12 11 9 11
11 9 10 13 11 12
12 10 11 10 13 9
9 14 9 10 10 11
10 10 9 11 12 11
10 7 10 10 10 8
样例输出
54
代码:
int task[15][15];
int n;//员工个数=任务个数(一个员工分配一个工作)
int ans=20000;
int vis[15];
void dfs(int index,int sum)//分配到第index个工作
{
int i;
if(index==n)
{
if(sum<ans)
{
ans=sum;
return ;
}
}
for(i=0;i<n;i++)
{
if(!vis[i])
{
vis[i]=1; //标记访问
dfs(index+1,sum+task[index][i]); //index+1 访问下一个员工,且sum+当前员工的时间
vis[i]=0; //访问结束,当前的员工标记为未访问!!!
}
}
}
int main()//全排列,求最小值
{
int i,j;
//task[i][j] 指的是第 i 项工作如果由 j 号员工完成所需要的时间。
cin>>n;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
cin>>task[i][j];
dfs(0,0);
cout<<ans<<endl;
return 0;
}
7. k个数的和
题目描述:
从n个数中选k个数的和为sum,共有多少种选取方式?
代码:
int sum,n,k,a[105],ans=0;
bool vis[105];
void dfs(int cnt,int s){
if(cnt==k&&s==sum){
ans++;
return;
}
for(int i=0;i<n;i++){
if(!vis[i]){
vis[i]=true;
dfs(cnt++,s+a[i]);
vis[i]=false;
}
}
}
int main() {
cin>>n>>k>>sum;
for(int i=0;i<n;i++){
cin>>a[i];
}
dfs(0,0);
cout<<ans<<endl;
return 0;
}
8. N皇后的问题
题目描述
一个如下的6×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,
每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列 2\ 4\ 6\ 1\ 3\ 52 4 6 1 3 5 来描述,第 ii 个数字表示在第 ii 行的相应位置有一个棋子,如下:
行号 1\ 2\ 3\ 4\ 5\ 61 2 3 4 5 6
列号 2\ 4\ 6\ 1\ 3\ 52 4 6 1 3 5
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 33 个解。最后一行是解的总个数。
输入格式
一行一个正整数 nn,表示棋盘是 n \times nn×n 大小的。
输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
输入输出样例
输入
6
输出
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4
解题思路:
把棋盘存储为一个N维数组a[N],数组中第i个元素的值代表第i行的皇后位置,这样便可以把问题的空间规模
压缩为一维O(N),在判断是否冲突时也很简单,首先每行只有一个皇后,且在数组中只占据一个元素的位置,
行冲突就不存在了,其次是列冲突,判断一下是否有a[i]与当前要放置皇后的列j相等即可。至于斜线冲突,
通过观察可以发现所有在斜线上冲突的皇后的位置都有规律即它们所在的行列互减的绝对值相等,
即| row – i | = | col – a[i] | 。这样某个位置是否可以放置皇后的问题已经解决。
代码:
#include<bits/stdc++.h>
using namespace std;
int ans[14],check[3][28]={0},sum=0,n;
void eq(int line)
{
if(line>n)//深搜完毕
{
sum++;
if(sum>3) return;//题目要求只输出字典序最小的三个
else
{//输出
for(int i=1;i<=n;i++) cout<<ans[i]<<' ';
cout<<endl;
return;//提速
}
}
for(int i=1;i<=n;i++)//遍历n列 ,保证字典序
{
if((!check[0][i])&&(!check[1][line+i])&&(!check[2][line-i+n])) //分别代表已经布局的列 和两个方向的对角线
{
ans[line]=i;
check[0][i]=check[1][line+i]=check[2][line-i+n]=1;
eq(line+1);//继续向下搜索
check[0][i]=check[1][line+i]=check[2][line-i+n]=0;//恢复 回溯
}
}
}
int main()
{
cin>>n;//n代表棋盘大小
eq(1);
cout<<sum;
return 0;
}
求解八皇后的问题
//求解八皇后的问题
/*
将棋盘用一维数组保存mp[i] 第i行mp[i]列;
可以保证每一行只放一个皇后,
剩下的就是判断列是否 列 对角线(对角线可由:行列相减绝对值相等)
*/
int ans=0;
bool col[10],x1[20],x2[20]; //col列 x1第一条对角线 x2第二条对角线
bool check(int r,int i){ //检查第r行i列是否被占用
return !col[i] && !x1[r+i] && !x2[r -i +8];
}
void dfs(int r){ //r代表行
if(r == 8){
ans++; //满足则+1
return;
}
for(int i=0;i<8;i++){
if(check(r,i)){ //判断第r行i列是否可以放置这个皇后
col[i]=x1[r + i]=x2[r-i+8]=true; //+8是因为r -i 可能为负数,加上一个偏移量没有影响
dfs(r+1); //下一行
col[i]=x1[r + i]=x2[r-i+8]=false; //要求出所有情况,标记取消
}
}
}
int main(){
dfs(0); //从第0行开始进行dfs
cout<<ans<<endl;
return 0;
}
9. 2n皇后
问题描述
给定一个n*n的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n小于等于8。
输入格式
输入的第一行为一个整数n,表示棋盘的大小。
接下来n行,每行n个0或1的整数,如果一个整数为1,表示对应的位置可以放皇后, 如果一个整数为0,表示对应的位置不可以放皇后。
输出格式
输出一个整数,表示总共有多少种放法。
样例输入
4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
样例输出
2
解题思路:
我们先摆放黑皇后(或者先摆放白皇后也可以),我们先定义一个棋盘map_Q[][],从标准输入流中定义,然后我们用两个一维数组posb[i],posw[i]分别表示棋盘的放置黑/白皇后的位置,如posb[i]=j,则表示棋盘上的第i列的黑皇后被放在第j行上。然后,我们同样根据n皇后的算法把所有的黑皇后放置完成,此时,我们在这样的基础上再放置白皇后,原理也跟n皇后的放置一样,只是多了一些判断能否放置的前提条件。当白皇后也完全放置时,结束一次大的递归循环,然后返回到黑皇后的dfs循环里面,在进行又一次大循环。
代码:
#include<cmath>
#include<iostream>
using namespace std;
const int maxn = 10;
int n;
int map_Q[maxn][maxn];
int posb[maxn]={0};
int posw[maxn]={0};
int ans;
bool checkw( int cur) //检查函数
{
for( int i = 1; i < cur; i++)
if( posw[i] == posw[cur] || abs(i-cur) == abs(posw[i]-posw[cur]))
return false;
return true;
}
bool checkb( int cur) //检查函数
{
for( int i = 1; i < cur; i++)
if( posb[i] == posb[cur] || abs(i-cur) == abs(posb[i]-posb[cur]))
return false;
return true;
}
void dfs_white( int cur)
{
if( cur == n+1) //白皇后也全部放完,次数+1
{
ans++;
}
for( int i = 1; i <= n; i++)
{
if( posb[cur] == i) //表示第cur列的第i行位置已经被黑皇后占用,
continue; //结束当前循环,i+1
if( map_Q[cur][i] == 0) //再判断前提条件是否成立
continue;
posw[cur] = i; //尝试把第cur列的白皇后放在第i行上
if( checkw(cur)) //判断能否放置白皇后
dfs_white(cur+1); //递归
}
}
void dfs_black( int cur)
{
if( cur == n+1) //当黑皇后处理完时,再处理白皇后
{
dfs_white(1);
}
for( int i = 1; i <= n; i++)
{
if( map_Q[cur][i] == 0) //如果第cur列第i行满足放皇后的前提条件即 mp[cur][i] == 1
continue; //如果不满足,则结束当前循环,进行下一次循环即i+1。
posb[cur] = i; //就尝试把第cur列的黑皇后放在第i行上
if( checkb(cur)) //然后判断该尝试是否成立,如成立,则进行递归,
//如不成立,则尝试把当前列的黑皇后放在下一行(i+1行)上。
dfs_black(cur+1); //递归
}
}
int main()
{
cin>>n;
for( int i = 1; i <= n; i++) //定义棋盘
for( int j = 1; j <= n; j++)
cin>>map_Q[i][j];
ans = 0;
dfs_black(1); //先把黑皇后放在第一列
cout<<ans<<endl;
return 0;
}
10. 方程的解数
题目描述:
int n,M;
int k[5],p[5];
int mypow[160][5];
int ans=0;
int poww(int x,int y){ //pow函数运行速度不快,手写一个函数
int ret=1;
for(int i=0;i<y;i++){
ret *= x;
}
return ret;
}
void dfs(int num,int sum)//dfs的过程
{
if(num!=0&&num!=n&&sum==0) //这一步可以省略
return ;
if(num==n){
if(sum==0) //只要有满足sum等于0,不管后续x为多少,ans+1,
ans++;
return ;
}
for(int i=1;i<=M;i++){
dfs(num+1,sum+k[num]*poww(i,p[num]));
}
}
int main()
{
cin>>n;
cin>>M;
for(int i=0;i<n;i++)
cin>>k[i]>>p[i];
dfs(0,0);
cout<<ans<<endl;
}
11. 数独
标准数独是由一个给与了提示数字的 9×9 网格组成,我们只需将其空格填上数字,
使得每一行,每一列以及每一个 3×3 宫都没有重复的数字出现。
输入:
* 2 6 * * * * * *
* * * 5 * 2 * * 4
* * * 1 * * * * 7
* 3 * * 2 * 1 8 *
* * * 3 * 9 * * *
* 5 4 * 1 * * 7 *
5 * * * * 1 * * *
6 * * 9 * 7 * * *
* * * * * * 7 5 *
输出:
1 2 6 7 3 4 5 9 8
3 7 8 5 9 2 6 1 4
4 9 5 1 6 8 2 3 7
7 3 9 4 2 5 1 8 6
8 6 1 3 7 9 4 2 5
2 5 4 8 1 6 3 7 9
5 4 7 2 8 1 9 6 3
6 1 3 9 5 7 8 4 2
9 8 2 6 4 3 7 5 1
代码:
/*
思路:先判断同一行和同一列,然后在判断每一个3*3九宫格中是否有重复数字
,这该如何判断呢,我给这9个3*3的九宫格编号,利用每个格子的坐标(最好从0开始,
这样更好编号)来编号,然后存储在数组中,只需判断每个编号中是否有数字重复即可
*/
#include<iostream>
using namespace std;
char map[10][10];
int n = 0;
int flag = 0;
int row[99];
int col[99];
int book[10][10] = {0};
void print()
{
for(int i = 1;i <= 9;++i)
{
for(int j = 1;j <= 9;++j)
{
if(j == 1)
cout << map[i][j];
else
cout << " " << map[i][j];
}
cout << endl;
}
}
bool check(int cnt,char num)
{
//检查行
for(int i = 1;i <= 9;++i)
{
if(i == col[cnt]) continue;
if(map[row[cnt]][i] == num)
return false;
}
//检查列
for(int i = 1;i <= 9;++i)
{
if(i == row[cnt]) continue;
if(map[i][col[cnt]] == num)
return false;
}
return true;
}
void dfs(int cnt)
{
if(flag) return ;
if(cnt == n)
{
print();
flag = 1;
return ;
}
for(int i = 1;i <= 9;++i)
{
int x1 = (row[cnt]-1)/3;
int x2 = (col[cnt]-1)/3;
int x3 = x1*3+x2;
if(check(cnt,i+'0') && !book[x3][i]){
book[x3][i] = 1;
map[row[cnt]][col[cnt]] = i+'0';
dfs(cnt+1);
map[row[cnt]][col[cnt]] = '*';
book[x3][i] = 0;
}
}
}
int main()
{
for(int i = 1;i <= 9;++i)
{
for(int j = 1;j <= 9;++j)
{
cin >> map[i][j];
//这里是为了计算编号,所以把横纵-1
int k1 = i-1;
int k2 = j-1;
int x1 = k1/3;
int x2 = k2/3;
int num = x1*3+x2;
if(map[i][j] == '*')
{
row[n] = i;
col[n++] = j;
}
else
book[num][map[i][j]-'0']++;
}
}
dfs(0);
return 0;
}
12. 引爆炸弹
在一个 n×m 的方格地图上,某些方格上放置着炸弹。手动引爆一个炸弹以后,炸弹会把炸弹所在的行和列上的所有炸弹引爆,被引爆的炸弹又能引爆其他炸弹,这样连锁下去。现在为了引爆地图上的所有炸弹,需要手动引爆其中一些炸弹,为了把危险程度降到最低,请算出最少手动引爆多少个炸弹可以把地图上的所有炸弹引爆。
输入格式
第一行输两个整数 n,m,用空格隔开。
接下来 n 行,每行输入一个长度为 m 的字符串,表示地图信息。0表示没有炸弹,1表示炸弹。
数据约定:
对于 60% 的数据:1≤n,m≤100;
对于 100% 的数据:1≤n,m≤1000;
数据量比较大,不建议用cin输入。
输出格式
输出一个整数,表示最少需要手动引爆的炸弹数。
样例输入
5 5
00010
00010
01001
10001
01000
样例输出
2
代码1:
#include<stdio.h>
#include<string.h>
#include<algorithm>
int n,m;
char map[2000][2000];
int h[5000],l[5000];
void dfs(int x,int y)
{
map[x][y]='0';
int i,j;
if(h[x]==0)
{
for(i=0;i<m;i++)
if(map[x][i]=='1')//寻找第x行中是否含有1
{
h[x]=1;//将这一行进行标记,下次在调用时就无需在判断这一行
dfs(x,i);
}
}
if(l[y]==0)
{
for(i=0;i<n;i++)
{
if(map[i][y]=='1')//寻找第y列中是否含有1
{
l[y]=1;//将这一列进行标记,下次在调用时就无需在判断这一列
dfs(i,y);
}
}
}
}
int main()
{
int i,j,sum;
while(~scanf("%d%d",&n,&m))
{
sum=0;
memset(h,0,sizeof(h));
memset(l,0,sizeof(l));
for(i=0;i<n;i++)
scanf("%s",map[i]);
for(i=0;i<n;i++)
for(j=0;j<m;j++)
if(map[i][j]=='1')
{
sum++;
dfs(i,j);
}
printf("%d\n",sum);
}
return 0;
}
代码2:
int n,m,cnt=0;
int s[1005][1005];
bool vx[1005],vy[1005]; //这一行,这一列 有没有访问过
void dfs(int x,int y){ //这个dfs将符合条件的s[x][y]标记为0
s[x][y]=0;
if(!vx[x]){
vx[x]=true;
for(int i=0;i<m;i++){
if(s[x][i]==1){
dfs(x,i);
}
}
}
if(!vy[y]){
vy[y]=true;
for(int i=0;i<n;i++){
if(s[i][y]==1){
dfs(i,y);
}
}
}
}
int main(){
cin>>n>>m; //n行m列
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>s[i][j];
}
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(s[i][j]==1){
cnt++;
dfs(i,j);
}
}
}
cout<<cnt<<endl;
return 0;
}
用例:
5 5
0 0 0 1 0
0 0 0 1 0
0 1 0 0 1
1 0 0 0 1
0 1 0 0 0
输出:2