4.3 深度广度搜索

水管连接

题干

我们有一张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") ; 
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值