• Tempter of the Bone(奇偶剪枝)


    一看就知道是神搜,但是弄了好长时间一直是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
    "); 
         else{
         find(startrow,startline);
        printf("%s
    ",(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
    ",(mark==0?"NO":"YES")); 
    	}
      return 0;
    }
    





  • 相关阅读:
    sql server的for xml path与变通的行转列
    nginx产生【413 request entity too large】错误的原因与解决方法
    spring的15个经典面试题
    数据库死锁预防规范
    服务端高并发分布式架构的演进
    后端接口统一返回响应对象
    数据库的dml、ddl和dcl的概念
    [na]ip routing&no ip routing
    [na]一站式学习wireshark
    [na]tcpdump参数应用参考
  • 原文地址:https://www.cnblogs.com/zswbky/p/5432012.html
Copyright © 2020-2023  润新知