题解
A - 迷宫问题
题目:定义一个二维数组:
int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。
- DFS解决迷宫问题
- 直接代码分析
#include <iostream>
#include <vector>
#include <string.h>
using namespace std;
int m(5),n(5);
int vis[20][20];//标记数组,去掉已经经过的地点
int f[4][2] = {{0,-1},{-1,0},{0,1},{1,0}};//方向数组,用在选择方向时的坐标处理
int a[20][20];
vector<pair<int,int> > q, qq;//和结构体有些类似,头文件<vector>;第一个q是队列,第二个用来储存最短路径的队列
int xb(0),yb(0),xe(4),ye(4);//起始坐标与终点坐标
int minn(100);
bool judge(int x1,int y1)//判断下一个方向的坐标是否合法
{
return (x1>=0&&x1<m&&y1>=0&&y1<n&&vis[x1][y1]==0&&a[x1][y1]==0);
}
void dfs(int x,int y, int ss)//!!!这里的ss是对每一条路径长度记录用的
{
if(x == xe&&y== ye)
{
if(ss < minn)
{
minn = ss;
qq = q;
}
return ;
}
for(int i=0; i<4; i++)
{
int x1 = x+f[i][0];
int y1 = y+f[i][1];
if(judge(x1,y1))
{
vis[x1][y1]=1;
pair<int,int> z(x1,y1);
q.push_back(z);//这一句和上一句是用来把新的坐标放进队列
dfs(x1,y1,ss+1);
vis[x1][y1]=0;//标记回溯
q.pop_back();//队列回溯
}
}
}
int main()
{
memset(a,0,sizeof a);
memset(vis,0,sizeof vis);//数组初始化,头文件<string.h>
for(int i=0; i<5; i++)
for(int j=0; j<5; j++)
cin>>a[i][j];
vis[xb][yb]=1;
pair<int,int> z(xb,yb);
q.push_back(z);
dfs(xb,yb, 1);
for(int i=0; i<qq.size(); i++)
{
cout<<"("<<qq[i].first<<", "<<qq[i].second<<")\n";
}
return 0;
}
B - 马走日
题目:马在中国象棋以日字形规则移动。编写一段程序,给定n*m大小的棋盘,以及马的初始位置(x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点。
- DFS遍历所有点
- 走向下一点的方向从四个变成了八个,DFS里面的判断和标记也有所变化(遍历所有点与从某一点到另一点的区别)
- 方向数组
int f[8][2]=
{
{1,2},{1,-2},{-1,2},{-1,-2},{2,1},{2,-1},{-2,1},{-2,-1}
};
- DFS函数
void dfs(int a,int b,int ss)//ss依旧记录路径长度(或者说坐标点数量)
{
if(ss==n*m)//当一条路径经过的坐标点数量达到棋盘上点的数量时,记录路径数目的s加一
s++;
if(map[a][b]==0)
{
map[a][b]=1;//标记数组
for(int i=0; i<8; i++)
{
int newx=a+f[i][0];
int newy=b+f[i][1];
if(newx<0||newx>=n||newy<0||newy>=m||map[newx][newy]==1)//判断下一个点是否合法的部分
continue;
dfs(newx,newy,ss+1);
}
map[a][b]=0;
}
return ;
}
C - Prime Ring Problem
题目:环由n个圆组成,如图所示。将自然数1,2,…,n分别放入每个圆中,两个相邻圆中的数字之和应为素数。
- DFS遍历所有情况
- 注意点:1.第一个圆的数量应始终为1。2.不要忘了最后一个数还要和第一个数放一起判断一下。
- 这里有一种方法就是把0~n的数字用0/1数组判断是不是素数。没太大必要,算是尝试新东西。
#include<iostream>
#include<cmath>
#include<string.h>
using namespace std;
int a[100], vis[100];
int n,prime[100];//直接prime把数字弄成01组,减少判断函数的调用
bool isPrime(int x)
{
for (int i=2; i<=sqrt(x+0.0); i++)
{
if (x%i==0)
return false;
}
return true;
}
void dfs(int x)
{
if (x==n&&prime[a[1]+a[n]])//最后一个数与第一个数的判断
{
for(int i=1; i<n; i++)
cout<<a[i]<<" ";
cout<<a[n]<<endl;
return ;
}
else
{
for(int i=2; i<=n; i++)
{
if (!vis[i]&&prime[a[x]+i])//m是假的,n是真的才成立
{
a[x+1]=i;
vis[i]=1;
dfs(x+1);
vis[i]=0;
}
}
}
return ;
}
int main()
{
int t(0);
while(cin >> n)
{
t++;
for(int i=2; i<=n*2; i++)
prime[i]=isPrime(i);
cout<<"Case "<<t<<":"<<endl;
memset(vis,0,sizeof(vis));
a[1] = 1;
dfs(1);
cout<<endl;
}
return 0;
}
D - Sticks
题目:取相同长度的棍子若干并随意切割,直到所有部分最多变成50个单位。现在想把棍子归还原来的状态,但是不知道原来有多少棍子和原来多长。计算这些棒可能的最小原始长度。
- DFS+剪枝
- 都标记在代码里了,很详细,剪枝方法有参考别人
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int n;
int maxx, sum;
int temp;
int a[100];
int vis[100];
int dfs(int cnt, int index, int ss)
{
if (cnt == n)//结束点,当棒棒的数目等于题目中的n
return 1;
for (int i = index ; i < n; i++)
{
if (vis[i])continue;//标记点呐。。
if (ss + a[i] < temp)
{
vis[i] = 1;
if (dfs(cnt + 1, i + 1, ss + a[i]))//不能组成temp的话就加一继续组
return 1;
vis[i] = 0;
while(a[i] == a[i + 1] && i + 1 < n) i++;//这一根都不能实现其他的等长也不能实现。。。为什么还超时
}
else if (ss + a[i] == temp)//做成一个temp就要重新组了
{
vis[i] = 1;
if (dfs(cnt + 1, 0, 0))
return 1;
vis[i] = 0;
return 0;//如果当前木棒与后面所有木棒都拼不上,直接返回false;
}
if(ss == 0)//这一根都没用上自然结束了
return 0;
}
return 0;
}
int cmp(int a, int b)
{
return a > b;
}
int main()
{
while (cin >> n&&n)
{
maxx = 0;
sum = 0;
memset(a, 0, sizeof a);
memset(vis, 0, sizeof vis);
for (int i = 0; i < n; i++)
{
cin >> a[i];
sum += a[i];//得到总长
maxx = max(maxx, a[i]);//这对棒子里最长的棒棒
}
sort(a , a + n , cmp);//之后,a[i]都是降序的了,从大的开始找也是为了减少时间
for (int i = n; i > 0; i--)//从n开始尝试,因为n越大,单个小棒越小
{
temp = sum/i; //temp指的是可能的长度
if (sum % i != 0 || temp < maxx)continue;//筛选:不能被整除或者单个长度小于maxx
else
{
memset(vis, 0, sizeof vis);
if (dfs(0, 0, 0))
{ cout << temp << endl; break; }
}
}
}
return 0;
}
F - Tempter of the Bone
题目:迷宫是一个大小为N×M的矩形。迷宫中有一扇门。在开始时,门被关闭,它将在第T秒开启一小段时间(不到1秒)。因此,小狗必须在第T秒才到达门口。在每一秒中,他可以将一个块移动到上,下,左和右相邻块之一。一旦他进入一个区块,这个区块的地面将开始下沉并在下一秒消失。他不能在一个街区停留一秒以上,也不能进入一个被访问的街区。判断小狗能否活下来?
- 有条件的走迷宫问题
- DFS+剪枝
- DFS部分将前几题的稍微改动就行,难点还在剪枝。
#include<iostream>
#include<cstring>
using namespace std;
int n, m;
int t;
int xe, ye, xd, yd;
bool flag = false;
char a[8][8];
int vis[8][8];
int nex[4][2] = { {1,0},{0,-1},{0,1},{-1,0} };
void dfs(int x, int y, int cnt)
{
if (flag) return;//也试了几种剪枝的思路,还不如这一句有用
if (x == xe && y == ye)
{
if (cnt == t) flag = true;
return;
}
for (int i = 0; i < 4; i++)
{
int tx = x + nex[i][0];
int ty = y + nex[i][1];
if (tx > m || tx<1 || ty>n || ty < 1 ||vis[tx][ty] == 1)
continue;
else
{
if (vis[tx][ty] == 0 && a[tx][ty] == '.' || a[tx][ty] == 'D')
{
vis[tx][ty] = 1;
dfs(tx, ty, cnt + 1);
vis[tx][ty] = 0;
}
}
}
return;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);//又见到了
while (cin >> m >> n >> t && m && n && t)
{
memset(vis, 0, sizeof vis);
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
{
cin >> a[i][j];
if (a[i][j] == 'D')
{ xe = i; ye = j; }
if (a[i][j] == 'S')
{ xd = i; yd = j; }
}
vis[xd][yd] = 1;
flag = false;
dfs(xd, yd, 0);
if (flag)
{
cout << "YES" << endl;
}
else
cout << "NO" << endl;
}
}
本文通过几个具体的例子,介绍了如何使用深度优先搜索(DFS)算法来解决迷宫寻路、中国象棋马走日问题、素数环问题等经典问题。文章详细解析了DFS算法的应用场景和实现细节,帮助读者掌握该算法的精髓。


2101

被折叠的 条评论
为什么被折叠?



