• HDU 1175 连连看 (DFS+剪枝)


    <题目链接>

    题目大意:
    在一个棋盘上给定一个起点和终点,判断这两点是否能通过连线连起来,规定这个连线不能穿过其它的棋子,并且连线转弯不能超过2次。

    解题分析:
    就是DFS从起点开始搜索,只不过搜索的时候需要记录当前的方向和已经转弯的次数,然后通过题目给定的限制条件进行搜索,判断是否存在从起点到终点转弯次数不超过2次的连线。同时,在转弯次数达到两次的时候,我们可以对搜索树进行可行性剪枝,直接判断转弯两次后,该点与终点是否在同一条直线上,从而减少搜索时间。

    #include <bits/stdc++.h>
    using namespace std;
    
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    const int N= 1e3+5;
    int mpa[N][N],vis[N][N];
    int n,m,sx,sy,ex,ey;
    const int dir[][2]={1,0,0,1,-1,0,0,-1};
    bool fp;
    
    void dfs(int x,int y,int nowdir,int cnt){
        vis[x][y]=1;
        if(cnt>2||fp)return;
        if(x==ex&&y==ey){ fp=true;return; }
        for(int k=0;k<4;k++){
            int cnt1=cnt;
            int nx=x+dir[k][0],ny=y+dir[k][1];
            if(nx<1||nx>n||ny<1||ny>m||vis[nx][ny])continue;
            if((nx==ex&&ny==ey)||!mpa[nx][ny]){
                if((nowdir!=-1)&&nowdir!=k){
                    cnt1++;if(cnt1==2){
                        if((nx-ex)!=0&&(ny-ey)!=0)return;   //如果第二次转弯与终点不在同一条直线上
                        else{   //转了2次后,判断该点是否能够通过这条直线到达终点
                            int xx=nx,yy=ny;
                            while(xx>=1||xx<=n||yy>=1||yy<=m){
                                if(xx==ex&&yy==ey){ fp=true; return; }    //如果通过该直线能够直接到达终点
                                if(mpa[xx][yy]!=0)return;      //如果中途除终点外有障碍物
                                xx=xx+dir[k][0],yy=yy+dir[k][1];    //沿着这条直线往前走
                            }
                        }
                    }
                }
                dfs(nx,ny,k,cnt1);
            }
        }
    }
    
    int main(){
        while(~scanf("%d%d",&n,&m),n||m){
            rep(i,1,n) rep(j,1,m) scanf("%d",&mpa[i][j]);
            int q;scanf("%d",&q);
            while(q--){
                scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
                if(sx<1||sx>n||sy<1||sy>m||ex<1||ex>n||ey<1||ey>m||(sx==ex&&sy==ey)){puts("NO");continue;}    //判断这两点的位置合不合法
                fp=false;
                memset(vis,0,sizeof(vis));
                if(mpa[sx][sy]==mpa[ex][ey]&&mpa[sx][sy])dfs(sx,sy,-1,0);      //如果起点终点相同,且有棋子
                fp?puts("YES"):puts("NO");
            }
        }
    }
    WA代码

    AC代码:

    #include <bits/stdc++.h>
    using namespace std;
     
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    const int N = 1e3+5;
    int mpa[N][N];
    bool vis[N][N];
    int n,m,q,sx,sy,ex,ey;
    bool flag;
    const int dicx[]={1,-1,0,0};
    const int dicy[]={0,0,1,-1};
     
    void dfs(int x,int y,int dic,int cnt){
        if(cnt>2||flag) return;   //转弯次数大于2或者已经找到就终止 
        if(cnt==2&&(x-ex)!=0&&(y-ey)!=0) return;//剪枝:判断两次转弯后是否与目标在同一直线上 
        if(x==ex&&y==ey){ flag=true;return; }
        for(int k=0;k<4;++k){
            int xx=x+dicx[k],yy=y+dicy[k];
            if(xx<1||xx>n||yy<1||yy>m||vis[xx][yy]) continue;
            if(mpa[xx][yy]==0||(xx==ex&&yy==ey)){
                vis[xx][yy]=1;
                if(dic==-1||dic==k)dfs(xx,yy,k,cnt);
                else dfs(xx,yy,k,cnt+1);
                vis[xx][yy]=0;
            }
        }
    }
    int main(){
        while(~scanf("%d%d",&n,&m),n||m){
            rep(i,1,n) rep(j,1,m) scanf("%d",&mpa[i][j]);
            scanf("%d",&q);
            while(q--){
                scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
                memset(vis,0,sizeof(vis));
                flag=false;
                if(mpa[sx][sy]==mpa[ex][ey]&&mpa[sx][sy])dfs(sx,sy,-1,0);    //初始方向置为-1
                flag?puts("YES"):puts("NO");
            }
        }
    }
  • 相关阅读:
    [HNOI2012]永无乡
    [HNOI2011]括号修复 / [JSOI2011]括号序列
    [NOI2005]维护数列
    文艺平衡树(Splay)
    SP2713 GSS4
    SP1716 GSS3
    Cookie和Session
    Session和Cookie的区别与联系
    Django ORM 多对多操作 使用聚合函数和分组 F查询与Q查询
    csrf攻击
  • 原文地址:https://www.cnblogs.com/00isok/p/10528819.html
Copyright © 2020-2023  润新知