杭電1010--Tempter of the Bone(DFS+奇偶剪枝)
阿新 • • 發佈:2018-11-28
【題目描述】
就是類似於迷宮問題,給出出發點、終點、限定時間(不能早到或者晚到)、牆壁。走過的地方不能再走,求能否在指定時間到達。
【分析】
很標準的DFS題,但是程式碼提交上去不是TLE就是WA了,學習了一下其他人的程式碼,發現自己的問題如下:
- 起點沒有設定為1,這導致了小狗可能走回起點
- 剪枝不徹底,導致程式執行TLE
奇偶剪枝
設兩點分別為 st(a,b)、en(c,d)
求兩點間最短到達時間很簡單:min=abs(a-c)+abs(b-d)
先給出奇偶剪枝的結論:最短時間min和指定時間t同奇或同偶時,才能使用t時間從st到en。
推導:
假設有迷宮如下:(借鑑了別人的圖片和思路, 連結)
從s到e的黑色路徑為一條最短路徑,紅色和藍色路線組成的路徑,為走出最短路徑的路徑。其中藍色箭頭是參雜著的最短路徑中的操作,只有紅色箭頭才是走出去和拐回來的路徑,如果將紅色路徑去掉,從s向右走,經過綠色箭頭到達e,這也是一條最短路徑。
因為a=2b,是個偶數,又因為a=t-m,所以當t和m的奇偶性相同時,a才能是偶數。也就是說,當t和m的奇偶性相同時,才有可能從s經過t步,到達e。
所以,當最小步數m與t同為奇數,或同為偶數時,才有可能從s經過t步,到達e。
【程式碼】
另外還有一點需要注意,在DFS裡也應該加上時間判斷,避免超時。
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; struct pos { int x, y; }st, en; const int maxn = 1000 + 10; int m, n, t, vis[maxn][maxn], flag = 0; int dir[4][2] = { {-1,0},{1,0},{0,1}, {0,-1} }; void DFS(int cur, int x, int y) { if (x == en.x&&y == en.y&&cur == t) { flag = 1; return; } if (cur >= t) return; //下面開始遞迴列舉四個方向 int temp = = t - abs(st.x - en.x) - abs(st.y - en.y); if (temp < 0)return; for (int i = 0;i < 4;i++) { int t1 = x + dir[i][0]; int t2 = y + dir[i][1]; if (!vis[t1][t2] && t1 >= 1 && t1 <= n && t2 >= 1 && t2 <= m) { vis[t1][t2] = 1; DFS(cur + 1, t1, t2); if (flag) return; vis[t1][t2] = 0; } } } void init() { flag = 0; memset(vis, 0, sizeof(vis)); char ch; for (int i = 1;i <= n;i++) { for (int j = 1;j <= m;j++) { cin >> ch; if (ch == 'S') { st.x = i;st.y = j; vis[i][j] = 1; } else if (ch == 'D') { en.x = i;en.y = j; } else if (ch == 'X') vis[i][j] = 1; } } } int main() { while (~scanf("%d%d%d", &n, &m, &t) && (m || n || t)) { getchar(); init(); int temp = t - abs(st.x - en.x) - abs(st.y - en.y); if (temp < 0 || temp & 1) { cout << "NO\n"; continue; } DFS(0, st.x, st.y); cout<<(flag?"YES":"NO")<<endl; } return 0; }