题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=1072
方法:和1180一样,图中有些位置是所有的搜索路径都可以去走的,题中就是还原装置所在的位置。除此之外还有其他需要注意的地方。
1.所有还原装置的位置都不能被设置为已经访问而不能再去访问,这和1180中的楼梯一样。
2.一条收索路径经过一个还原装置后,不能再回到该还原装置,因为会产生环路,如下地图:
2 1 4 1
2 1 0 3
到从1行2列的1穿过1行3列的还原装置4到达1行4列的1时,由于还原装置的位置如上述第一点说的那样永远不会被设置为已经访问,所以在1行4列的1这个位置广搜的话,会探寻到还原装置的位置以带来重复无用的搜索。所以这里要采用1180中使用的那种方式来剪枝,来阻止这里的1行4列的1这个位置的广搜把还原装置重新加入搜索队列。
3.该题和最基本广搜题还有一个很大的不同,那就是在基本的广搜题,一个点在开始从其进行广搜的时候,如果发现已经被设为访问了,那说明之前有条更短的路径提前到达了该点,从该点已经进行的比当前试图要进行的搜索还有优越的搜索,所以,剪枝,不再搜索,而判断的是否被访问根据的状态只是单纯的坐标。 而该题中不然,如下面的地图:
1 2
1 0
1 4
1 0
1 0
1 0
1 0
3 0
当从1行2列的起始点2搜索到3行1列的点位1时,其做两个方向的搜索,一是消耗一单位时间,直接向下,二是去3行2列的还原装置那里获取更多的时间,此时如果仅仅靠坐标来确定需不需要再搜索的时候,就会出现问题,因为搜索到3行2列的还原装置那里时,已经不能回到3行1列的位子了,这样即使使用了还原装置获取了足够的跳脱时间,也没有意义了,而向下的那个路径,虽然不需要回到3行1列的位子,但其时间会在跳脱前消耗完。所以搜索失败。
因此在单纯的坐标确定一个状态的基础上,再添加一个信息,那就是当前位子还剩余的时间,visitied[x][y][t]表示在(x,y)时间还剩t的状态是不是已经收索过,这样就能避免上述的情况。
这样处理的话,会有一个问题,如下地图,
2 1 1 0 1 1 4 0
1 0 4 1 1 0 1 3
从1行1列的2出发,会搜索到1行2列和2行1列的两个1,而这两个1其中有一个会在重新探寻到1行1列的起点的时候在该起点又开启一轮无用的广搜,因为当前探寻到起点已经和初始之前的起点状态不一样了,因为只是坐标一样但剩余不一样。然后再从起点 又一次探寻周围的两个1并又一次开始一个广搜,再然后这个两个1又会搜索到起点,起点又反过来再有一次搜索到那2个1,造成无用的循环搜索。不过还好,因为剩余时间在该过程中不断消耗,最终会停止。
感想:方法有待优化,还要注意一个wa的粗心错误,那就是第一次找到目标的时候没有跳出优先队列的循环,还在继续搜索,导致后续非最优的结果覆盖了第一次搜索到的最优结果。
代码:
#include <iostream> #include <queue> using namespace std; int n,m; bool visited[10][10][7]; int map [10][10]; int derection[4][2]; int timeDeadLine = 6; enum locationType{wall,road,start,target,bombReset}; struct Step { int x,y; int currentUsedTime; int timeRemaine; }; struct cmp { bool operator()(Step x,Step y) { if(x.currentUsedTime>y.currentUsedTime) return true; return false; } }; bool canGo(int x,int y,int t) { return map[x][y]!=wall && (!visited[x][y][t] || map[x][y]==bombReset); } bool bombRestUsed[10][10][10][10]; int search(int startX, int startY) { Step step; step.x = startX; step.y = startY; step.timeRemaine = timeDeadLine; step.currentUsedTime = 0; priority_queue<Step,vector<Step>,cmp> q; q.push(step); int re = -1; while(!q.empty()) { Step t_step = q.top(); q.pop(); if(!visited[t_step.x][t_step.y][t_step.timeRemaine]) { visited[t_step.x][t_step.y][t_step.timeRemaine] = ( map[t_step.x][t_step.y] == bombReset ? false:true); if(map[t_step.x][t_step.y]==target) { re = t_step.currentUsedTime; break; } else { Step n_step; if(t_step.timeRemaine-1>0) { for(int i =0;i<4;i++) { int n_x = t_step.x+derection[i][0]; int n_y = t_step.y+derection[i][1]; int timeRemain = map[n_x][n_y] == bombReset ? timeDeadLine:t_step.timeRemaine-1 ; if( map[t_step.x][t_step.y] == bombReset) bombRestUsed[t_step.x][t_step.y][n_x][n_y]=true; if(canGo(n_x,n_y,timeRemain)) { Step n_step; n_step.currentUsedTime = t_step.currentUsedTime+1; n_step.x=n_x; n_step.y=n_y; n_step.timeRemaine = timeRemain; if(map[n_step.x][n_step.y]!=bombReset || (map[n_step.x][n_step.y]==bombReset && !bombRestUsed[n_step.x][n_step.y][t_step.x][t_step.y])) q.push(n_step); } } } } } } return re; } void main() { derection[0][0] = 0;derection[0][1] = 1; derection[1][0] = 1;derection[1][1] = 0; derection[2][0] = 0;derection[2][1] = -1; derection[3][0] = -1;derection[3][1] = 0; int tc=0,t; scanf("%d",&t); while(tc<t) { scanf("%d %d",&n,&m); memset(bombRestUsed,false,sizeof(bombRestUsed)); memset(map,wall,sizeof(map)); memset(visited,false,sizeof(visited)); int stx,sty; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf("%d",&map[i][j]); if(map[i][j]==start) { stx=i; sty=j; } } cout<<search(stx,sty)<<endl; tc++; } }