HDU 1010 DFS+奇偶剪枝

Tempter of the Bone

做DFS(或其他搜索题),我感觉最有趣的地方不是DFS本身而是——“剪枝”。剪枝,顾名思义就是剪去不必要的枝节,也就是避免不必要的搜索过程。有点类似于工程领域的“去噪”,当然这是个人感觉而已。。

HDU 1010这道题是一个典型的迷宫搜索题。给你出口入口,但是你并不是能找到出口就完事了。注意事项:

  • 必须在给定的时间 t 的时候找到。不能多于t 也不能少于 t
  • 入口S 并不是每次都在 左上角(0,0)的位置,也可能是其他位置

分析DFS

前面有篇博文《hdu1518》,已经提到了DFS的一般框架就是:循环+递归。

循环,是横着走。递归是竖着走。注意,我这里说的“横”和“竖”并不是对应本题中迷宫的物理意义上的横竖,而是关于 抽象的逻辑意义上的

  • 竖:是从某点出发,每一步都是走当前步的后继结点
  • 横:用于“竖”走不通的时候,比如没有后继,或后继都搜索过的情况下,此时去搜索当前结点的兄弟结点,换句话说就是回溯到当前结点的父节点,再选取父节点中没有走过的“岔路”。

本题中 的 横 是 某一结点周围的 上、下、左、右 四个方向。

本题中的 竖 就是递归搜索每一结点的 “横”。

终止态:是所走的步数(即题意中的时间)等于给出的时间。若此时还未走到出口,则返回fasle。找到出口则返回true。

本题中我没有使用一个[4][2]的二维数组来保存 上下左右 四个方位的移动。所以看起来代码有些繁琐,但是可读性却高。

bool dfs(int ti,int i,int j)
{
	if(ti==t)//ti是当前步数,t是给出的步数,或者说时间
	{
		if(i==h&&j==w)
			return true;
		return false;
	}
	if(j<m-1&&!ma[i][j+1])
	{
		ti++;//步数+1
		ma[i][j]=true;//迷宫中该点设为已走
		if(ti<=t&&dfs(ti,i,j+1))
			return true;
		ti--;//若走此步最终失败了,那么就步数-1
		ma[i][j]=false;//再设为该点未走,即回溯的过程
	}
	if(i<n-1&&!ma[i+1][j])
	{
		ti++;
		ma[i][j]=true;
		if(ti<=t&&dfs(ti,i+1,j))
			return true;
		ti--;
		ma[i][j]=false;
	}
	if(i&&!ma[i-1][j])
	{
		ti++;
		ma[i][j]=true;
		if(ti<=t&&dfs(ti,i-1,j))
			return true;
		ti--;
		ma[i][j]=false;
	}
	if(j&&!ma[i][j-1])
	{
		ti++;
		ma[i][j]=true;
		if(ti<=t&&dfs(ti,i,j-1))
			return true;
		ti--;
		ma[i][j]=false;
	}
	return false;
}

剪枝

本题代码简单但是很容易超时,所以要注意剪枝。

首先看理论上的最短路径。

int path = abs(si-h)+abs(sj-w);

(si,ji)、(h,w)分别为起点终点坐标。我们可使用的两种剪枝方案是:

  1. 如果所给的时间(步数) t 小于最短步数path,那么一定走不到。
  2. 若满足t>path。但是如果能在恰好 t 步的时候,走到出口处。那么(t-path)必须是二的倍数。

关于第二种方案的解释:

这种方案学名为“奇偶剪枝”。我们已知了最短的步数就是直角三角形的两条直角边,实际上的路径却不一定非要沿着这两条边走的。仔细看看只要是移动方向一直是右、下,那么走到的时候总步数也一定是path的。然而由于墙的存在或许我们不可能一直右、下的走下去。为了避开墙,我们可能会向左走,向上走等等。但为了到达目的地,你在最短步数的基础上,如果向右走了一步,那么某一时候也必须再向左走一步来弥补。所以(t-path)一定要是2的倍数。

if(t<path||(t-path)%2)
{
	cout<<"NO"<<endl;
	continue;
}

看完整代码:

#include <iostream>
using namespace std;
bool ma[10][10];
int h,w;
int n,m,t;
bool dfs(int ti,int i,int j)
{
	if(ti==t)
	{
		if(i==h&&j==w)
			return true;
		return false;
	}
	if(j<m-1&&!ma[i][j+1])
	{
		ti++;
		ma[i][j]=true;
		if(ti<=t&&dfs(ti,i,j+1))
			return true;
		ti--;
		ma[i][j]=false;
	}
	if(i<n-1&&!ma[i+1][j])
	{
		ti++;
		ma[i][j]=true;
		if(ti<=t&&dfs(ti,i+1,j))
			return true;
		ti--;
		ma[i][j]=false;
	}
	if(i&&!ma[i-1][j])
	{
		ti++;
		ma[i][j]=true;
		if(ti<=t&&dfs(ti,i-1,j))
			return true;
		ti--;
		ma[i][j]=false;
	}
	if(j&&!ma[i][j-1])
	{
		ti++;
		ma[i][j]=true;
		if(ti<=t&&dfs(ti,i,j-1))
			return true;
		ti--;
		ma[i][j]=false;
	}
	return false;
}
int main()
{//freopen("in.txt","r",stdin);
	while(cin>>n>>m>>t)
	{
		if(!n&&!m&&!t)
			break;
		memset(ma,false,sizeof(ma));
		char c;
		int si,sj;
		for(int i=0;i<n;i++)
			for(int j=0;j<m;j++)
			{
				cin>>c;
				switch(c)
				{
				case 'S':si=i;sj=j;ma[i][j]=true;break;
				case '.':ma[i][j]=false;break;
				case 'X':ma[i][j]=true;break;
				case 'D':ma[i][j]=false;h=i,w=j;break;
				}
			}
		int path = abs(si-h)+abs(sj-w);
		if(t<path||(t-path)%2)
		{
			cout<<"NO"<<endl;
			continue;
		}
		if(dfs(0,si,sj))
			cout<<"YES"<<endl;
		else
			cout<<"NO"<<endl;
	}
	return 0;
}

posted on 2014-04-14 14:08  果冻虾仁  阅读(121)  评论(0编辑  收藏  举报

导航