BFS(广度优先搜索)按照层次遍历所有的状态,使用队列实现较早得到的状态较先得到扩展。
DFS(深度优先搜索)按照前序遍历所有的状态,不使用堆栈而使用递归实现较早得到的扩展。
问题17:有一个N*M的迷宫,包括起点S,终点D,墙X和地面,0秒时主人公从S出发,每秒能走到四个与其相邻的位置中的一个,且每个位置被行走之后都不能再次走入,问是否存在这样一条路径能使主人公在T秒时刚好走到D。
include<stdio.h>
char mark[8][8];//保存地图信息
int n,m,t;//地图大小为n*m,从起点到终点能否恰好t秒
bool go[][2] = {-1,0,-1,0,0,1,0,-1};//四方向行走坐标差
void DFS(int x,int y,int time){
for(int i = 0;i < 4;i++){
int nx = x + go[i][0];
int ny = y + go[i][1];
if(nx < 1||nx > n||ny < 1||nv > m) continue;//若坐标在地图外则跳过
if(maze[nx][ny] == 'X') continue;//若该位置为墙,则跳过
if(maze[nx][ny] == 'D'){
if(time + 1 == t){//所用时间恰好为t
success = true;
return;
}
else continue;//否则该状态的后续不可能为答案(经过的点不可能再经过),跳过
}
maze[nx][ny] = 'x';//该状态扩展而来的后续状态中,该位置都不能被经过,直接修改该位置为墙
DFS(nx,ny,time+1);//递归扩展该状态,所用时间递增
maze[nx][ny] = '.';//若其后续状态全部遍历完毕,则退回到上层状态,将因为要搜索其后续状态而改成墙的位置,改回普通位置
if(sucess) return;
}
}
int main(){
while(scanf("%d%d%d",&n,&m,&t) != EOF){
if(n == 0 && m == 0 && t == 0) break;
for(int i = 1;i <= n;i++){
scanf("%s",maze[i] + 1);
}
success = false;
int sx,sy;
for(int i = 1;i <= n;i++){//寻找D的位置坐标
for(int j = 1;j < m;j++){
if(maze[i][j] == 'D'){
sx = i;
sy = j;
}
}
}
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(maze[i][j] == 'S' && (i + j)%2 == ((sx+sy)%2+t%2)%2){
//找到S点后,先判断S与D的奇偶性关系,是否和t符合,即符合上式,若不符合则直接跳过
maze[i][j] = 'X';//将起点标记为墙
DFS(i,j,0);//递归扩展初试状态
}
}
}
puts(sucess == true ? "YES":"NO");
}
return 0;
}
DFS各要素如下:
(a)搜索空间:依旧是所有的状态
(b)搜索目的:查找一个可以表示愿问题解的状态
(c)搜索方法:在解答树上进行先序遍历
问题18:汉诺塔
把最左边的所有盘移动到最右边(原始汉诺塔)
移动完毕
现在改变汉诺塔规则,不允许直接从最左(右)边移动到最右(左)边,每次移动一定是移动到中间柱或从中间柱移出,也不允许大盘放到小盘上面。假设有N(1<=N<=35)个圆盘,至少需要多少次移动才能把这些圆盘从最左边移动到最右边。
同理可知递归方式为:3F(n-1) + 2
#include<stdio.h>
#include<string.h>
Long long F(int num){//返回值较大使用long long类型
if(num == 1) return 2;
else return 3*F(num - 1) +2;
}
int main(){
int n;
while(scanf("%d",&n) != EOF){
printf("%lld\n",F(n));
}
return 0;
}
问题19:由给定的1到n数字中,将数字依次填入环中,使得环中任意两个相邻的数字间的和为素数。对于给定的n,按字典序由小到大输出所有符合条件的解(第一个数恒为1)。
采用回溯法枚举每一个值,当第一个数位为1确定时,尝试放入第二个数,使其和1的和为素数,放入后再尝试放入第三个数,使其与第二个数的和为素数,直到所有的数全部被放入环中,且最后一个数与1的和也是素数。为实现回溯枚举,采用递归的形式。
#include<stdio.h>
#include<string.h>
int ans[22];//保存环中每一个被放入的数
bool hash[22];//标记之前已经被放入环中的数
int n;
int prime[] = {2,3,5,7,11,13,17,19,23,29,31,37,41};//素数,因为输入不大于16,故两数和构成的素数必在该数组内
bool judge(int x){
for(int i = 0;i < 13;i++){
if(prime[i] == x) return true;
}
return false;
}
void check(){//检查输出由回溯法枚举得到的解
if(judge(ans[n]+ans[1]) == false) return;//判断最后一个数和第一个数是否为素数
for(int i = 1;i <= n;i++){
if(i != 1) printf(" ");
printf("%d",ans[i]);
}
printf("\n");
}
void DFS(int num){//递归枚举,num为当前已经放入环中的数字
if(num > 1)
if(judge(ans[num] + ans[num-1] == false)) return;
//判断最后两个数字是否为素数,若不是则返回继续枚举第num个数
if(num == n){
check();
return;
}
for(int i = 2;i <= n;i++){//放入一个数
if(hash[i] ==false){//若i还没有放入环中
hash[i] = true;//标记i为已使用
ans[num+1] = i;//将这个数字放入ans数组中
DFS(num+1);//继续尝试放入下一个数
hash[i] = false;//当回溯回枚举该位数字时,将i重新标记为未使用
}
}
}
int main(){
int cas = 0;
while(scanf("%d",&n)!=EOF){
cas++;
for(int i = 0;i < 22;i++) hash[i] = false;
ans[1] = 1;//第一个数字恒为1
printf("Case %d:\n",cas);
hash[i] = true;
DFS(1);//继续尝试放入下一个数字
printf("\n");
}
return 0;
}
}