题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=1010
题目描述:
根据地图,'S'为开始位置,'D'为门的位置,' . '为空地,'X'为墙,不能经过,问:在指定的时间,是否能到达'门'的位置。注意:路不可以重复经过,时间也要刚好是 t ,不能少.
思路:
此处不能用BFS,因为时间要恰好为t,还是得用DFS,不过需要剪枝才能过。
奇偶剪枝:
从一个点到达另外一个点的最短路径长度(时间)可以根据两点坐标求出,路径长度(非最短)与最短路径的长度同奇偶,它们的差一定是偶数!举个例子,就像两个偶数的差差是偶数,两个个数的差也是偶数.
此处还有一个剪枝:
设墙的数目为wall,如果wall + t >= n * m,一定到达不了,因为大于号显然成立,这里主要讨论等号的情况。
比如下图
3 3 1
SDX
XXX
XXX
只需要一步就可以到达,此时wall = 7, t = 1,wall + t < n * m,有可行解,这是由于有S,D的存在,所以如果有可行解,wall的数目一定会比n*m-t要小,等于的话是没有可行解的。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<string> 6 #include<queue> 7 using namespace std; 8 typedef long long ll; 9 const int INF = 1<<30; 10 const int maxn = 100000; 11 int T, cases; 12 int n, m, k, x1, y1, x2, y2; 13 char Map[10][10]; 14 int dir[4][2] = {0,1,1,0,-1,0,0,-1}; 15 bool dfs(int x, int y, int time)//到x1, y1点,花费时间time 16 { 17 if(x < 0 || x >= n || y < 0 || y >= m || time > k)return false; 18 //剪枝 19 if(time == k && x == x2 && y == y2)return true; 20 int mintime = k - time - abs(x - x2) - abs(y - y2); 21 if(mintime < 0)return false;//最优性剪枝,当前沿着最优路径走,还是会超过k秒,返回假 22 if(mintime & 1)return false;//奇偶剪枝,相减的时间是奇数的话,在k秒的时候一定到不了终点 23 24 for(int i = 0; i < 4; i++) 25 { 26 int xx = x + dir[i][0]; 27 int yy = y + dir[i][1]; 28 if(Map[xx][yy] != 'X') 29 { 30 Map[xx][yy] = 'X'; 31 if(dfs(xx, yy, time + 1))return true; 32 Map[xx][yy] = '.'; 33 } 34 } 35 return false; 36 } 37 int main() 38 { 39 while(cin >> n >> m >> k && (n + m + k)) 40 { 41 int wall = 0; 42 for(int i = 0; i < n; i++) 43 { 44 cin >> Map[i]; 45 for(int j = 0; j < m; j++) 46 { 47 if(Map[i][j] == 'S')x1 = i, y1 = j; 48 else if(Map[i][j] == 'D')x2 = i, y2 = j; 49 else if(Map[i][j] == 'X')wall++; 50 } 51 } 52 if(wall + k >= n * m) 53 ///这里是一个特别好的剪枝,如果墙的数目+步数>=n*m,一定不可能完成任务 54 ///重点考虑等号,除了墙还有'S'和'D',墙的数目最多为n*m-k-1(距离,比如k = 1时,S和D相邻,其他都是墙,此时墙的数目最多为n*m-2,满足上述式子) 55 { 56 printf("NO "); 57 continue; 58 } 59 Map[x1][y1] = 'X';//先设置成X表示该点不能再经过 60 if(dfs(x1, y1, 0))printf("YES "); 61 else printf("NO "); 62 } 63 }