迷宫中回溯法的剪枝——奇偶剪枝(hdu 1010 Tempter of the Bone)

转自:http://blog.csdn.net/code_pang/article/details/8839432

问题描述

在一个n行m列的迷宫中,每一步只能向上、下、左、右中任意方向走一格,迷宫中有围墙的地方是无法到达的。从起点s开始,能否刚好走t步,到达e。

例如在下面5行5列的迷宫中,能否恰好经过9步,从s走到e。初始位置在s上,#是围墙。



奇偶剪枝

设起点s的坐标为(sx,sy),终点e的坐标为(ex,ey);

对s的一次操作为对sx或sy进行+1或-1;

若经过t次操作后,s的坐标刚好等于e,则说从s经过t步可以到达e。

在理想情况下,s到e需要的最小步数为m

m=|ex-sx|+|ey-sy|

若t<m,肯定是无法到达的。

当t>=m时,从s到e的行走路径由两部分组成,

一部分为需要走的最少步数m;

另一部分是为了使得刚好行走t步到达e,所需要走的附加步数a,a=t-m;

这a步即为:从需要走m步的最短路径上走出去,再回到最短路径上所走的步数。

假设走出去这段路径长度为b,那回来时的路径长度一定也是b,因此,附加步数的路径长度a等于2b步。

因为走出去时,对坐标进行了b次+1或-1的操作,为使坐标再恢复到最短路径上,就需要进行b次-1或+1的操作,并且与走出去时是相反的。注意:走出去和拐回来的过程中可能参杂着最短路径上的操作,所以b是除去这些参杂操作后的步数。

如下图所示:


从s到e的黑色路径为一条最短路径,红色和蓝色路线组成的路径,为走出最短路径的路径。其中蓝色箭头是参杂着的最短路径中的操作,只有红色箭头才是走出去和拐回来的路径,如果将红色路径去掉,从s向右走,经过绿色箭头到达e,这也是一条最短路径。

因为a=2b,是个偶数,又因为a=t-m,所以当t和m的奇偶性相同时,a才能是偶数。也就是说,当t和m的奇偶性相同时,才有可能从s经过t步,到达e。

所以,当最小步数m与t同为奇数,或同为偶数时,才有可能从s经过t步,到达e

 #include<iostream>
#include<math.h>
#include<string.h>
#include<queue>
#include<memory.h>
using namespace std;
int n,m,t;
char a[100][100];
int visited[100][100];
int dir[4][2]={0,1,0,-1,1,0,-1,0};
int flag;
int sx,sy,ex,ey;
void dfs(int x,int y,int sum)
{
     int i,mx,my,d;
     if(flag==1)//找到了 
	   return;
     if(x==ex&&y==ey&&sum==t) //找到了 
	    {
		  flag=1;
		  return;
		}
     d=abs(x-ex)+abs(y-ey);//当前点到终点的最短距离       剪枝 
	 if(d>t-sum||(t-sum-d)%2!=0) return;//最短距离大于剩下的时间或者剩余的时间减去最短路径为奇数
	 for(i=0;i<4;i++)
	 {
	 	mx=x+dir[i][0];
	 	my=y+dir[i][1];
	 	if(mx>=0&&mx<n&&my>=0&&my<m&&visited[mx][my]==0&&a[mx][my]!='X')
	 	{
	 		visited[mx][my]=1;
	 		dfs(mx,my,sum+1);
	 		visited[mx][my]=0;
		 }
	 }	 
} 
int main()
{
    while(cin>>n>>m>>t)
    {
    	if(n==0&&m==0&&t==0) 
		   break; 
    	int temp=0;
        for(int i=0;i<n;i++)
          for(int j=0;j<m;j++)
          {
              cin>>a[i][j];
              if(a[i][j]=='S')   { sx=i; sy=j; }
              if(a[i][j]=='D')   { ex=i; ey=j;}
         }
         flag=0;
		 memset(visited,0,sizeof(visited));
		 visited[sx][sy]=1;
         dfs(sx,sy,0);
         if(flag==0)  cout<<"NO"<<endl;
         else         cout<<"YES"<<endl;
    }   
    return 0;
}


转载于:https://www.cnblogs.com/xunalove/p/6809253.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值