题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=1175
方法:在每一步都要维护4个信息,当前坐标,当前方向(垂直还是水平),当前已经折了多少次和携带的目标数字。根据这几个信息,在一步深搜索探寻下一步的时候,选择下一步的标准是:
没有访问过 && (是可以走的路 || (不是路而是一个和目标数字相等的数 && 该位置就是目标位置))。
整个算法的过程是:
1.获取输入后验证是否可达,标准为:
a.两个位子的数字必须相同。
b.两个位子的数字必须不能有0。
c.目标位置的直接周边4个必须至少有一个0.
如果三个条件不能都满足,直接输出NO。
2.上述条件都满足,可以开始深搜,首先要初始化。这里重点要设置以下4个数据:
a.可以 走纯水平路线到达目标的最左边的位子的列:pfromLeft
b.可以 走纯水平路线到达目标的最左边的位子的列:pfromRight
c.可以 走纯垂直路线到达目标的最上边的位子的列:pfromUp
d.可以 走纯垂直路线到达目标的最下边的位子的列:pfromDown
3.正式开始深搜。深搜过程中不断设置每一步上是当时维持的方向,再和上一步的方向比较得到当前改变方向的次数,也就是经过的折线个数。当折线个数超过2,则不从该位子进行深搜。而当折线个数不超过2刚好等于2的时候,重点剪枝就在这里:
a.如果当前的方向是水平的,但是目标位子在水平方向不可达,剪掉。
b.如果当前的方向是水平的,且目标位子在水平方向可达,但是当前位置和目标位置不在同一行,剪掉。
c.如果当前的方向是水平的,且目标位子在水平方向可达,且当前位置和目标位置在同一行,但当前位置不在pfromLeft 和 pfromRight的比区间内,剪掉。
d.如果当前的方向是水平的,且目标位子在水平方向可达,且当前位置和目标位置在同一行,且当前位置在pfromLeft 和 pfromRight的比区间内,搜索成功,直接返回成功,搜索结束。
e.如果当前的方向是垂直的,但是目标位子在垂直方向不可达,剪掉。
f.如果当前的方向是垂直的,且目标位子在垂直方向可达,但是当前位置和目标位置不在同一列,剪掉。
g.如果当前的方向是垂直的,且目标位子在垂直方向可达,且当前位置和目标位置在同一列,但当前位置不在pfromLeft 和 pfromRight的比区间内,剪掉。
h.如果当前的方向是垂直的,且目标位子在垂直方向可达,且当前位置和目标位置在同一列,且当前位置在pfromUp和 pfromDown的比区间内,搜索成功,直接返回成功,搜索结束。
感想:深感剪枝的重要性,不剪的话,题目给的10秒时间都要超时,剪了的话只要62ms.
代码:
#include <iostream> #include <queue> using namespace std; int n,m; int map[1002][1002]; bool visited[1002][1002]; int deriction[4][2]={{-1,0},{0,1},{1,0},{0,-1}}; int g_edx,g_edy; bool fromUp,fromDown,fromLeft,fromRight; int pfromUp,pfromDown,pfromLeft,pfromRight; struct step { int x; int y; int currentChangeDirection; bool isVertival; int carry; }; bool canGo(step cur,int x,int y) { return !visited[x][y] && (map[x][y]==0|| (map[x][y] == cur.carry && x ==g_edx && y ==g_edy)); } bool DFSSearch(step st,step ed) { int n_x,n_y; if(st.x == ed.x && st.y==ed.y) return true; visited[st.x][st.y]=true; for(int i=0;i<4;i++) //vertical true { n_x = st.x+deriction[i][0]; n_y = st.y+deriction[i][1]; if(canGo(st,n_x,n_y)) { step n_step; n_step.x = n_x; n_step.y = n_y; n_step.isVertival = i%2==0; n_step.carry = st.carry; if(st.currentChangeDirection ==-1) n_step.currentChangeDirection=0; else if(st.isVertival!=n_step.isVertival) n_step.currentChangeDirection = st.currentChangeDirection+1; else n_step.currentChangeDirection = st.currentChangeDirection; if(n_step.currentChangeDirection<=2) { if(n_step.currentChangeDirection==2) { if(n_step.isVertival && (fromDown || fromUp) && n_step.y == g_edy) { if(n_step.x>=pfromUp && n_step.x<=pfromDown) return true; } if(!n_step.isVertival && (fromLeft || fromRight) && n_step.x== g_edx) { if(n_step.y>=pfromLeft && n_step.y<=pfromRight) return true; } } else if(DFSSearch(n_step,ed)) return true; } } } visited[st.x][st.y]=false; return false; } bool search(int stx, int sty,int edx,int edy) { step st_step,ed_step; st_step.x =stx; st_step.y = sty; ed_step.x =edx; ed_step.y =edy; g_edx = edx; g_edy = edy; st_step.currentChangeDirection=-1; st_step.carry = map[stx][sty]; int t_edx=edx,t_edy = edy; while(map[t_edx+1][t_edy]==0) t_edx++; pfromDown = t_edx; t_edx=edx,t_edy = edy; while(map[t_edx][t_edy+1]==0) t_edy++; pfromRight = t_edy; t_edx=edx,t_edy = edy; while(map[t_edx][t_edy-1]==0) t_edy--; pfromLeft = t_edy; t_edx=edx,t_edy = edy; while(map[t_edx-1][t_edy]==0) t_edx--; pfromUp = t_edx; return DFSSearch(st_step,ed_step); } bool validate(int stx, int sty,int edx,int edy) { if(map[stx][sty] != map[edx][edy]) return false; if(map[stx][sty] ==0 || map[edx][edy]==0) return false; if(map[stx-1][sty] ==0) fromUp =true; if(map[stx][sty-1]==0) fromLeft = true; if( map[stx][sty+1]==0) fromRight = true; if(map[stx+1][sty]==0) fromDown = true; if(!fromUp && !fromLeft && !fromRight && !fromDown) return false; return true; } void main() { int tc; while(scanf("%d %d",&n,&m) && n+m!=0) { memset(map,-1,sizeof(map)); for(int i =1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&map[i][j]); scanf("%d",&tc); int stx, sty, edx, edy; for(int i =0;i<tc;i++) { memset(visited,false,sizeof(visited)); scanf("%d %d %d %d",&stx,&sty,&edx,&edy); if(!validate(stx, sty, edx, edy)) cout<<"NO"<<endl; else { if(search(stx, sty, edx, edy)) cout<<"YES"<<endl; else cout<<"NO"<<endl; } } } }