4.3 深度广度搜索

本文介绍了一种基于深度优先搜索(DFS)的水管连接路径寻找算法。该算法可以在一张N*M的地图上,根据两种类型的水管(直管和弯管),从起点(1,1)铺设到终点(N,M),并能输出可能的路径或者返回'impossible'。文章详细阐述了如何通过跟踪水管的进出水口方向来筛选合适的水管类型,并给出了完整的C语言代码实现。

水管连接

题干

我们有一张N*M大小的地图
我们想要把水管从 (1,1)的铺设起点 铺到 (N,M)的铺设终点
其中我们有两种水管,弯管和直管,他们都可以自由地90°旋转
其中弯管可以连接上下左右任意两个相邻位置,直管则连接相对位置
计算连接方式,如果可以连接则输出路径,否则输出impossible

思路

其实本质上还是我们的深度/宽度搜索,额外添加了下面两个条件:
1.需要考虑进出水口的方向
2.需要给出路径

摆放情况

我们首先讨论摆放情况,有下面六种情况,我们也用1-6的编号表示它们:
1:上到右
2:下到右
3:左到下
4:右到上
5:左到右
6:上到下

状态分析

水管连接和之前的搜索比起来,最重要的一点就是条件
那么我们先结合DFS搜索,把对应的代码写出来:

    void dfs(int x , int y , int front){
        //注意这个front,它代表的是进水口的方向
        //我们在筛选的时候以进水口方向进行一次筛选
        //1左边,2上边,3右边,4下边

        //越界判断
        if(x<1 || x>n || y<1 || y>m){
            return ; 
        }

        //判断这个点是否已经被使用过    
        if(book[x][y] ==1){
            return ; 
        }

        book[x][y] = 1 ; //还没走过的话,就先标记这里

        //先分析水管为直管的情况
        if( a[x][y] >=5 && a[x][y]<=6){
            //5 6 代表直管的两个情况

            //按照左上右下的顺序进行分析
            if(front == 1){
                dfs(x,y+1,1) ; //只能使用5号
            }

            if(front == 2){
                dfs(x+1,y,2) ; //6
            }

            if(front == 3){
                dfs(x,y-1,3) ; //5
            }

            if(front == 4){
                dfs(x-1,y,4) ; //6
            }

        }

        book[x][y] ; //因为是DFS算法,所以要记得取消标记哦
        return ; 
    }

对于弯管的处理,其原理是一样的,这里我们就不赘述了
另外,当我们到达(n,m+1)这个点时,就表明产生了完整方案——可以返回了

        //判断是否到达终点的Base case   
        if(x==n && y==m+1){
            flag = 1 ; // 标记找到了方案
            return ; 
        }
完整代码(无路径返回)

我们一步步来,先得到不返回路径,只产生方案的搜索:

    #include<stdio.h>
    int a[51][51] ; 
    int book[51][51] ,n,m,flag = 0 ;
    void dfs(int x , int y , int front){
        //注意这个front,它代表的是进水口的方向
        //我们在筛选的时候以进水口方向进行一次筛选
        //1左边,2上边,3右边,4下边

        //判断是否到达终点的Base case   
        if(x==n && y==m+1){
            flag = 1 ; // 标记找到了方案
            return ; 
        }

        //越界判断
        if(x<1 || x>n || y<1 || y>m){
            return ; //碰到了就之前爬
        }

        //判断这个点是否已经被使用过    
        if(book[x][y] ==1){
            return ; //走过了那也爬
        }

        book[x][y] = 1 ; //还没走过的话,就先标记这里

        //先分析水管为直管的情况
        if( a[x][y] >=5 && a[x][y]<=6>){
            //5 6 代表直管的两个情况

            //按照左上右下的顺序进行分析
            if(front == 1){
                dfs(x,y+1,1) ; //只能使用5号
            }

            if(front == 2){
                dfs(x+1,y,2) ; //6
            }

            if(front == 3){
                dfs(x,y-1,3) ; //5
            }

            if(front == 4){
                dfs(x-1,y,4) ; //6
            }

        }

        //分析弯管,和直管原理一样  
        if( a[x][y]>=1 && a[x][y]<=4){
            if(front == 1){
                dfs(x+1,y,2) ; //3
                dfs(x-1,y,4) ; //4
            }

            if(front == 2){
                dfs(x,y+1,1) ; //1
                dfs(x,y-1,3) ; //4
            }

            if(front == 3){
                dfs(x-1,y,4) ; //1
                dfs(x+1,y,2) ; //2
            }
            
            if(front == 4){
                dfs(x,y+1,1) ; //2
                dfs(x,y-1,3) ; //3
            }
        }

        book[x][y] ; //因为是DFS算法,所以要记得取消标记哦
        return ; 
    }

    //在main方法中执行我们的方案
    int main(){
        int i,j,num = 0 ;
        scanf("%d %d",&n,&m);

        //地图读入
        for(i=1;i<=n:i++)
            for(j=1;j<=m;j++)
                scanf("%d",&a[i][j]);   

        //开始搜索,从(1,1)开始
        dfs(1,1,1);

        //根据flag的最后情况分析结果
        if(flag==0)     printf("impossible \n");
        else            printf("找到了方案 \n") ; 
    }

啊,“找到方案”,对,我们只是找到了方案
想要让这个做法真正有意义,我们就应该输出相应的路径

增加路径的返回

这一步比想象中简单————毕竟我们已经一步步找到了结果了
我们只要在代码中加入一个,就可以利用它输出路径了

    struct note{
        int x ; //横坐标
        int y ; //纵坐标
    }
    s[100];

完整代码(含路径的返回)

    #include<stdio.h>
    int a[51][51] ; 
    int book[51][51] ,n,m,flag = 0 ;

    //-----------------------------------------
    struct note{
        int x ; //横坐标
        int y ; //纵坐标
    }
    s[100];
    //-----------------------------------------

    void dfs(int x , int y , int front){
        //注意这个front,它代表的是进水口的方向
        //我们在筛选的时候以进水口方向进行一次筛选
        //1左边,2上边,3右边,4下边

        //判断是否到达终点的Base case   
        if(x==n && y==m+1){
            flag = 1 ; // 标记找到了方案
            return ; 
        }

        //越界判断
        if(x<1 || x>n || y<1 || y>m){
            return ; 
        }

        //判断这个点是否已经被使用过    
        if(book[x][y] ==1){
            return ; 
        }

        book[x][y] = 1 ; //还没走过的话,就先标记这里

        //将标记入栈
        //-----------------------------------------
        top++ ; 
        s[top].x = x;
        s[top].y = y ;
        //-----------------------------------------

        //先分析水管为直管的情况
        if( a[x][y] >=5 && a[x][y]<=6){
            //5 6 代表直管的两个情况

            //按照左上右下的顺序进行分析
            if(front == 1){
                dfs(x,y+1,1) ; //只能使用5号
            }

            if(front == 2){
                dfs(x+1,y,2) ; //6
            }

            if(front == 3){
                dfs(x,y-1,3) ; //5
            }

            if(front == 4){
                dfs(x-1,y,4) ; //6
            }

        }

        //分析弯管,和直管原理一样  
        if( a[x][y]>=1 && a[x][y]<=4){
            if(front == 1){
                dfs(x+1,y,2) ; //3
                dfs(x-1,y,4) ; //4
            }

            if(front == 2){
                dfs(x,y+1,1) ; //1
                dfs(x,y-1,3) ; //4
            }

            if(front == 3){
                dfs(x-1,y,4) ; //1
                dfs(x+1,y,2) ; //2
            }
            
            if(front == 4){
                dfs(x,y+1,1) ; //2
                dfs(x,y-1,3) ; //3
            }
        }

        book[x][y] = 0  ; //因为是DFS算法,所以要记得取消标记哦
        //出栈---------------------------------------
        top -- ;
        //-------------------------------------------
        return ; 
    }

    //在main方法中执行我们的方案
    int main(){
        int i,j,num = 0 ;
        scanf("%d %d",&n,&m);

        //地图读入
        for(i=1;i<=n:i++)
            for(j=1;j<=m;j++)
                scanf("%d",&a[i][j]);   

        //开始搜索,从(1,1)开始
        dfs(1,1,1);

        //根据flag的最后情况分析结果
        if(flag==0)     printf("impossible \n");
        else            printf("找到了方案 \n") ; 
    }

好的,我会尽力回答你的问题。 首先,让我来解释一下我是如何工作的。ChitGPT 是一个基于 GPT 的聊天型 AI,能够进行自然语言的理解和生成。它可以根据用户的输入和上下文,生成有意义、合适的回答。但是,它并不是一个完全自主的 AI,需要有人为的干预和指导。 现在回到你的问题上来。你想让我实现一个输入无向图的顶点数、边数及各条边的顶点对,建立用邻接表表示的无向图,并对图进行深度优先搜索和广度优先搜索遍历,并分别输出其遍历序列。 以下是一个可能的实现方法: 1. 输入无向图的顶点数和边数,并根据这些信息创建一个邻接表。 2. 输入各条边的顶点对,并根据这些信息更新邻接表。 3. 对于每个顶点,将其邻接表中的节点按照编号从小到大排序。 4. 深度优先搜索4.1. 初始化一个空的栈,将第一个顶点入栈。 4.2. 对于每个节点,标记其为已访问。 4.3. 对于当前节点的每个未被访问的邻居节点,将其加入栈中,并标记为已访问。 4.4. 重复步骤 4.3,直到栈为空。 5. 广度优先搜索: 5.1. 初始化一个空的队列,将第一个顶点入队。 5.2. 对于每个节点,标记其为已访问。 5.3. 对于当前节点的每个未被访问的邻居节点,将其加入队列中,并标记为已访问。 5.4. 重复步骤 5.3,直到队列为空。 6. 输出深度优先搜索和广度优先搜索的遍历序列。 希望这个回答能够帮到你,如果你还有其他的问题,可以随时问我。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值