考研机试第九天

这篇博客探讨了三种不同的算法问题:使用深度优先搜索(DFS)解决迷宫路径问题,分析汉诺塔的移动次数,并利用回溯法解决环形数字填充,确保相邻数字之和为素数。每种问题都通过递归和回溯策略进行求解,展示了算法在解决复杂问题中的应用。
摘要由CSDN通过智能技术生成

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;
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值