在描述题目前先刷一下成就感:
看着就是很普通的过了,耗时也比较长,但是中间的艰辛只有自己明白,真的惨(大概中间错了有10-20次吧):其中经历了数组越界访问->答案错误->答案超时->答案错误->答案正确。这里会将我遇到的所有问题都给出来并提出解决办法。首先题目描述请点击这个hdu1175。(代码最后贴出)
要求大概就是两个:1、满足同类且有路径连接的两点可以互相消去 2、该路径折弯的次数不能超过两次。
思路如下:对于输入的起点和终点,通过某种方式判断两个点是否存在路径,该路径是否折弯2次以上。
首先,对于同类两点进行判定(不同类的如:1和2直接剔除,不用进行后续操作)。其次选用bfs或dfs对两点进行路径搜索,并在中途判定折弯次数是否超过两次,超过则剪枝。
遇到的失误:
-
记录路径数组的值设的太小了(实际上是跑的过程中测试例有一个超级大的点),导致了数组越界。解决办法:将数组的path[10000]改成path[1000000]。
-
一开始的时候,我是在两点间所有的路径跑完后才判断路径是否折弯,这就导致了如果两个点有多条路径,它可能会只记录一条不可行(两次以上的折弯)的路径并输出,因为它搜寻到路径以后就会直接退出递归。验证是否有这个问题可以用这个测试例:
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 -
接下来就是超时的问题了,光是折弯两次这个条件剪得还不够,因为可能存在折了一/两次弯以后有一段特别长的直线才到终点,这样会耗时很久,导致超时。经过这个分析,能够想到一种方法:折了两次弯以后肯定不会再折弯了,所以终点一定在这条直线上。按此来剪枝就可以不再超时了。(具体见代码,有注释)
-
最后就是答案错误的问题了,这个问题相当蠢:题目要求输出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的过程中如何记录真实路径(去除被舍弃的节点):记录节点+递归函数+记录减一(回溯时减少一个记录)。