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");
        }
    }
}
原文地址:https://www.cnblogs.com/00isok/p/10528819.html