再战 HDU 逃离迷宫

9 篇文章 0 订阅

之前做过这道题目,是用BFS做的,当时可能是觉得DFS会超时,但是这个题目一看就是赤裸裸的DFS思想,做出来之后超时,但是贼心不死,感觉只要BFS  DFS都是相通的,用BFS可以,肯定DFS也可以。

所以决定在剪枝上下功夫客服超时

 

剪枝思路:

题目的思路我就不说了,稍微懂得一点搜索的人都知道这个最普通的迷宫题目。一开始我是用VIS标记数组去标记走过的路来进行剪枝,但是,超时,肯定不用想了,出题人就是避免你简单的用DFS之后水过。

我是利用每一个点都有一个拐弯次数来进行剪枝的,因为这个不要求最佳路径,但是限制了拐弯次数,没走一步,每到一个定点,都会有一个相应的拐弯次数。如果超过了,那么即使到了终点也不可行。

if(tx<0||tx>=m||ty<0||ty>=n)            //判断是否超出边界//
			continue ;	

这里就是为了判断是否走出边界,如果走出就不用判断下面的,直接continue,寻找下一步


if(map[tx][ty]=='*'||wan[tx][ty]<wan[x][y])   //判断下一步是否为围墙,或者下一步的拐弯数小于当前的拐弯数,说明下一个点已经走过了,因为拐弯数是随着你走的不熟递增的//
因为限制条件一共有三个,一个边界,一个围墙,一个拐弯次数,所以这里的语句是后面两个

这种方法的剪枝巧妙就巧妙在这里,他不是用VIS数组去标记之前走过的路,而是用拐弯次数来标记

这样用一个数组,一次判断解决了两个问题,肯定会节省时间。我们知道,拐弯的次数是一次递增的,如果我们发现我们下一个要走的点比我们当前所占的点的拐弯次数还小,那么证明我们早就走过这个点了。



if(fangxiang!=-1&&i!=fangxiang&&wan[tx][ty]<wan[x][y]+1)   //如果不是起点,但是下一步要转弯,并且下一步的拐弯数小于当前步数+1(因为要拐弯)//
			continue;


这个语句是处理了起点的情况,因为我初始化每个点的拐弯次数是一个最大值,但是从起点出发的任意一步都不算做是一次拐弯

所以在判断的时候我们可以忽略起点,那么非起点的点,如果他的下一步需要拐弯,如果他的下一步的拐弯次数小于当前点拐弯次数+1,那么说明下一个要拐进去的点也是已经走过了的。

这样的两个IF就已经把所有可能走之前走过的点的情况全部处理。实现了VIS数组的作用。



if(fangxiang!=-1&&i!=fangxiang)     //如果有方向变化的话才加,fangxiang!=-1表示当前的站的点不是起点  i!=fangxiang 表示要拐弯,因为从起点出来之后,fangxiang的数值就变了//
			wan[tx][ty]++;
		dfs(tx,ty,i);

这里呢,就是判断方向开始计算拐弯次数了,如果不是起点,而且我们取的行走的方向和原来的方向不同,那么就当前的点的拐弯次数+1,并且处理下一步的时候,把当前的方向也要传过去。

方向的表示就用的是编号,因为在存向四个方向走的时候,相应的方向在数组中就有了相应的编号



另外,这一句话,也节省了很多的时间:

if(flag)
            return ;



好了,这个题目重要的就是剪枝,下面接代码:



//逃离迷宫//
//题目链接:http://acm.hdu.edu.cn/webcontest/contest_showproblem.php?pid=1003&ojid=0&cid=5201&hide=0
#include<stdio.h>
#include<string.h>
#define inf 0x3fffffff //定义一个无穷大的步数,用于步数的剪枝//
int wan[110][110];
char map[110][110];
int x_move[4] = {-1, 0, 1, 0};
int y_move[4] = {0, 1, 0, -1};
int flag=0;
int m,n;
int zhuanwan;
int s1,e1,s2,e2;
void dfs(int x,int y,int fangxiang)
{
	int tx,ty;
	int i;
	if(x==s2&&y==e2)
	{
		if(wan[x][y]<=zhuanwan)
		flag=1;
		return ;
	}
	if(x!=s2&&y!=e2&&wan[x][y]==zhuanwan)
		return ;
	if(wan[x][y]>zhuanwan)
		return ;
	for(i=0;i<4;i++)
	{
		tx=x+x_move[i];
		ty=y+y_move[i];
		if(tx<1||tx>m||ty<1||ty>n)            //判断是否超出边界//
			continue ;	
		if(map[tx][ty]=='*'||wan[tx][ty]<wan[x][y])   //判断下一步是否为围墙,或者下一步的拐弯数小于当前的拐弯数,说明下一个点已经走过了,因为拐弯数是随着你走的不熟递增的//
			continue;
		if(fangxiang!=-1&&i!=fangxiang&&wan[tx][ty]<wan[x][y]+1)   //如果不是起点,但是下一步要转弯,并且下一步的拐弯数小于当前步数+1(因为要拐弯)//
			continue;
		wan[tx][ty]=wan[x][y];
		if(fangxiang!=-1&&i!=fangxiang)     //如果有方向变化的话才加,fangxiang!=-1表示当前的站的点不是起点  i!=fangxiang 表示要拐弯,因为从起点出来之后,fangxiang的数值就变了//
			wan[tx][ty]++;
		dfs(tx,ty,i);
		if(flag)
			return ;
	}

}
int main()
{
	int i,j,k;
	int N;
	//int flag=0;
	scanf("%d",&N);
	while(N--)
	{
		scanf("%d%d",&m,&n);
		for(i=1;i<=m;i++)
			scanf("%s",map[i]+1);
		scanf("%d%d%d%d%d",&zhuanwan,&e1,&s1,&e2,&s2);
		for(i=1;i<=m;i++)
			for(j=1;j<=n;j++)
				wan[i][j]=inf;
		wan[s1][e1]=0;
		flag=0;
		dfs(s1,e1,-1);
		if(flag)
			printf("yes\n");
		else
			printf("no\n");

	}
	return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值