Sweety

Practice makes perfect

导航

Tempter of the Bone(奇偶剪枝)

Posted on 2015-02-07 00:29  蓝空  阅读(165)  评论(0)    收藏  举报

一看就知道是神搜,但是弄了好长时间一直是TimeLimited,开始的时候有剪枝的意识,但是全是小修小补,包括搜索到符合条件的及时回溯等方式,都还是不行,查资料才知道还有奇偶剪枝这回事,感觉很强大

关于奇偶剪枝:

应用范围:

给定你起点S,和终点D,之间有障碍,判断是否能在 T 时刻恰好到达终点D(下面的例题就是经典)

原理解释:

是数据结构的搜索中,剪枝的一种特殊小技巧。
现假设起点为(sx,sy),终点为(ex,ey),给定t步恰好走到终点,
 
s        
|        
|        
|        
+ e
 
如图所示(“|”竖走,“—”横走,“+”转弯),易证abs(ex-sx)+abs(ey-sy)为此问题类中任意情况下,起点到终点的最短步数,记做step,此处step1=8;
  
s  
  +  
| +      
|        
+ e
 
如图,为一般情况下非最短路径的任意走法举例,step2=14;
step2-step1=6,偏移路径为6,偶数(易证);
故,若t-[abs(ex-sx)+abs(ey-sy)]结果为非偶数(奇数),则无法在t步恰好到达;
返回,false;
反之亦反。

典型应用:题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1010

本题思路:深度搜索,对上下左右四个方向进行深搜,深搜前注意剪枝就好了...

#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
int sum=1, row,line,doorrow,doorline,ttime,mark=0;//mark为是否可行的标记 
char ch[10][10]={0};
void find(int i,int j){
   if(mark==0){
    if(sum==ttime&&doorrow==i&&doorline==j){    
        mark=1;
    }
    else if(sum<ttime&&mark==0){
       if(i>0&&(ch[i-1][j]=='.'||ch[i-1][j]=='D')){//向上深搜
    //  cout<<"上移"<<" i:"<<i-1<<' '<<"j:"<<j<<" sum:"<<sum+1<<" time "<<time<<endl;
        sum++;
        ch[i-1][j]='X';
        find(i-1,j);
        ch[i-1][j]='.';//还原
        sum--;         //还原
       }
       if(mark==1)
         return ;
      if(i<row-1&&(ch[i+1][j]=='.'||ch[i+1][j]=='D')){//向下深搜
        ch[i+1][j]='X';
        sum++;
        find(i+1,j);
        ch[i+1][j]='.';
        sum--;
        }
         if(mark==1)
            return ;
      if(j>0&&(ch[i][j-1]=='.'||ch[i][j-1]=='D')){////向左深搜
       ch[i][j-1]='X';   
       sum++;
       find(i,j-1);
       ch[i][j-1]='.';
       sum--;
     }
        if(mark==1)
          return ;
     if(j<line-1>0&&(ch[i][j+1]=='.'||ch[i][j+1]=='D')){//向右深搜
        ch[i][j+1]='X';
        sum++;
        find(i,j+1);
        ch[i][j+1]='.';
        sum--;
     }
    }
   }
}
int main (){
    int startrow,startline;
    while(~scanf("%d%d%d",&row,&line,&ttime),line,row,ttime){
        sum=0;
        mark=0;
     for(int i=0;i<row;i++){
     scanf("%s",ch[i]);
     }
     for(int i=0;i<row;i++) {
      for(int j=0;j<line;j++){
        if(ch[i][j]=='D')
            doorrow=i,doorline=j;
        if(ch[i][j]=='S')
            startrow=i,startline=j; 
      }
     }
     if( (ttime-(abs(doorrow-startrow)+abs(doorline-startline)))%2!=0)//奇偶剪枝 
       printf("NO\n"); 
     else{
     find(startrow,startline);
    printf("%s\n",(mark==0?"NO":"YES")); 
     }
    }
  return 0;
}

本来感觉下面的在每一次深搜时都剪枝,应该比上面只在第一次的时候剪枝要优化,效率更高,但是提交时却时间更长,感觉应该是测试数据小的原因

第一种时间:

第二种时间:

代码:

#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
int sum=1, row,line,doorrow,doorline,ttime,mark=0,startrow,startline;//mark为是否可行的标记 
char ch[10][10]={0};
void find(int i,int j){
    if(mark==1)  return; 
    if( (ttime-(abs(doorrow-startrow)+abs(doorline-startline)))%2!=0)//奇偶剪枝,且每次进入都剪枝 
	   return ;
	if(sum==ttime&&doorrow==i&&doorline==j){    
	    mark=1;
	}
	else if(sum<ttime&&mark==0){
	   if(i>0&&(ch[i-1][j]=='.'||ch[i-1][j]=='D')){
	    sum++;
	    ch[i-1][j]='X';
	    find(i-1,j);
	    ch[i-1][j]='.';
	    sum--;
	   }
	   if(mark==1)
         return ;
   	  if(i<row-1&&(ch[i+1][j]=='.'||ch[i+1][j]=='D')){
		ch[i+1][j]='X';
    	sum++;
	    find(i+1,j);
	    ch[i+1][j]='.';
	    sum--;
	    }
	    if(mark==1)
         return ;
   	  if(j>0&&(ch[i][j-1]=='.'||ch[i][j-1]=='D')){
	   ch[i][j-1]='X';   
	   sum++;
	   find(i,j-1);
	   ch[i][j-1]='.';
	   sum--;
   	  }
   	  if(mark==1)
         return ;
   	 if(j<line-1>0&&(ch[i][j+1]=='.'||ch[i][j+1]=='D')){
		ch[i][j+1]='X';
		sum++;
		find(i,j+1);
		ch[i][j+1]='.';
		sum--;
   	  }
   	  if(mark==1)
         return ;
    }
}
int main (){
	while(~scanf("%d%d%d",&row,&line,&ttime),line,row,ttime){
	sum=0,mark=0;	//别忘了每次都要初始化 
	for(int i=0;i<row;i++)
	   scanf("%s",ch[i]);
	for(int i=0;i<row;i++) 
	for(int j=0;j<line;j++){
	if(ch[i][j]=='D')
	   	doorrow=i,doorline=j;
	if(ch[i][j]=='S')
	    startrow=i,startline=j;	
	} 
	find(startrow,startline);
	 printf("%s\n",(mark==0?"NO":"YES")); 
	}
  return 0;
}