DFS(回溯加剪枝)解决HDU1175-连连看

在描述题目前先刷一下成就感:
在这里插入图片描述
看着就是很普通的过了,耗时也比较长,但是中间的艰辛只有自己明白,真的惨(大概中间错了有10-20次吧):其中经历了数组越界访问->答案错误->答案超时->答案错误->答案正确。这里会将我遇到的所有问题都给出来并提出解决办法。首先题目描述请点击这个hdu1175。(代码最后贴出)

要求大概就是两个:1、满足同类且有路径连接的两点可以互相消去 2、该路径折弯的次数不能超过两次。

思路如下:对于输入的起点和终点,通过某种方式判断两个点是否存在路径,该路径是否折弯2次以上。
首先,对于同类两点进行判定(不同类的如:1和2直接剔除,不用进行后续操作)。其次选用bfs或dfs对两点进行路径搜索,并在中途判定折弯次数是否超过两次,超过则剪枝。

遇到的失误

  1. 记录路径数组的值设的太小了(实际上是跑的过程中测试例有一个超级大的点),导致了数组越界。解决办法:将数组的path[10000]改成path[1000000]。

  2. 一开始的时候,我是在两点间所有的路径跑完后才判断路径是否折弯,这就导致了如果两个点有多条路径,它可能会只记录一条不可行(两次以上的折弯)的路径并输出,因为它搜寻到路径以后就会直接退出递归。验证是否有这个问题可以用这个测试例:
    4 5
    0 0 0 0 0
    0 3 4 1 4
    0 2 4 2 1
    0 0 0 0 0
    1
    3 2 3 4

  3. 接下来就是超时的问题了,光是折弯两次这个条件剪得还不够,因为可能存在折了一/两次弯以后有一段特别长的直线才到终点,这样会耗时很久,导致超时。经过这个分析,能够想到一种方法:折了两次弯以后肯定不会再折弯了,所以终点一定在这条直线上。按此来剪枝就可以不再超时了。(具体见代码,有注释)

  4. 最后就是答案错误的问题了,这个问题相当蠢:题目要求输出YES/NO,我输出Yes/No。经典错误用来警示一下自己。

最后贴上代码:

#include<bits/stdc++.h>
using namespace std;
int Map[1001][1001];
int vis[1001][1001];
int x,y,n,counted,maxcounted;//x为行,y为列,n为指令个数
int turns;//转向次数
bool found;//是否搜索成功
int dir[4][2]={//方向向量    
{-1,0},    
{0,-1},    
{1,0},    
{0,1}
};
char d[4]={'u','l','d','r'};
char path[1000000];//路径记录
char get_char(int sx,int sy,int nx,int ny){    
char s;    
if(sx-1==nx&&sy==ny)s=d[0];    
if(sx==nx&&sy-1==ny)s=d[1];    
if(sx+1==nx&&sy==ny)s=d[2];    
if(sx==nx&&sy+1==ny)s=d[3];    
return s;
}
bool check_path(char path[],int counted){//判断是否折弯超过两次    
turns=0;    
for(int i=0;i<counted-1;i++){        
if(path[i]!=path[i+1])turns++;   
 }    
 if(turns<=2)return true;    
 else return false;
 }
void DFS(int sx,int sy,int ex,int ey){   
if(sx==ex&&sy==ey){found=true;maxcounted=counted;return;}   
for(int j=0;j<4;j++){        
int nx=sx+dir[j][0];        
int ny=sy+dir[j][1];        
char s=get_char(sx,sy,nx,ny);        
if(nx<=x&&nx>=1&&ny<=y&&ny>=1&&vis[nx][ny]==0){            
if(Map[nx][ny]==0||(nx==ex&&ny==ey)){            
vis[nx][ny]=1;            
path[counted++]=s;            
if(check_path(path,counted)){//剪枝:折弯次数大于2的直接停止递归                
if(turns==2&&(nx!=ex&&ny!=ey));//再剪枝:折弯次数等于2后是否与终点同行或同列                    
else DFS(nx,ny,ex,ey);            
}            
if(!found)counted--;//回溯则路径记录减一            
vis[nx][ny]=0;//重置访问数组以免以后的路径与其重叠            
}        
}    
}
}
int main(){    
while(~scanf("%d%d",&x,&y)){        
if(x==0&&y==0)break;        
int sx,sy,ex,ey;        
for(int i=1;i<=x;i++)            
for(int j=1;j<=y;j++)scanf("%d",&Map[i][j]);        
scanf("%d",&n);        
for(int i=0;i<n;i++){            
found=false;            
counted=0;            
memset(path,0,sizeof(path));            
memset(vis,0,sizeof(vis));            
cin>>sx>>sy>>ex>>ey;            
if(Map[sx][sy]==0||Map[ex][ey]==0){printf("NO\n");continue;}            
if(Map[sx][sy]!=Map[ex][ey]){printf("NO\n");continue;}            
vis[sx][sy]=1;            
DFS(sx,sy,ex,ey);            
if(found){printf("YES\n");}           
else {printf("NO\n");}                 
}    
}    
return 0;}

学习到的新知识:在dfs的过程中如何记录真实路径(去除被舍弃的节点):记录节点+递归函数+记录减一(回溯时减少一个记录)。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值