1. 程式人生 > >杭電1010--Tempter of the Bone(DFS+奇偶剪枝)

杭電1010--Tempter of the Bone(DFS+奇偶剪枝)

【題目描述】
就是類似於迷宮問題,給出出發點、終點、限定時間(不能早到或者晚到)、牆壁。走過的地方不能再走,求能否在指定時間到達。
【分析】
很標準的DFS題,但是程式碼提交上去不是TLE就是WA了,學習了一下其他人的程式碼,發現自己的問題如下:

  1. 起點沒有設定為1,這導致了小狗可能走回起點
  2. 剪枝不徹底,導致程式執行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;
}