分支限界

Tempter of the Bone
Time limit: 1 Seconds   Memory limit: 32768K  
Total Submit: 4226   Accepted Submit: 824  

The doggie found a bone in an ancient maze, which fascinated him a lot. However, when he picked it up, the maze began to shake, and the doggie could feel the ground sinking. He realized that the bone was a trap, and he tried desperately to get out of this maze.
The maze was a rectangle with sizes N by M. There was a door in the maze. At the beginning, the door was closed and it would open at the T-th second for a short period of time (less than 1 second). Therefore the doggie had to arrive at the door on exactly the T-th second. In every second, he could move one block to one of the upper, lower, left and right neighboring blocks. Once he entered a block, the ground of this block would start to sink and disappear in the next second. He could not stay at one block for more than one second, nor could he move into a visited block. Can the poor doggie survive? Please help him.
Input
The input consists of multiple test cases. The first line of each test case contains three integers N, M, and T (1 < N, M < 7; 0 < T < 50), which denote the sizes of the maze and the time at which the door will open, respectively. The next N lines give the maze layout, with each line containing M characters. A character is one of the following:
'X': a block of wall, which the doggie cannot enter; 
'S': the start point of the doggie; 
'D': the Door; or
'.': an empty block.
The input is terminated with three 0's. This test case is not to be processed.
Output
For each test case, print in one line "YES" if the doggie can survive, or "NO" otherwise.
Sample Input
4 4 5
S.X.
..X.
..XD
....
3 4 5
S.X.
..X.
...D
0 0 0

Sample Output

NO
YES

Author: 
ZHANG, Zheng
----------------------------------------------------------------------------------------------
url:http://acm.zju.edu.cn/show_problem.php?pid=2110
解题思路:
迷宫问题的升级版,要求在规定步数到达出口,早一步或晚一步都不行,而且走过的路不能再走了
普通的走迷宫方法,并判断步数,在迷宫规模小于等于5*5的时候可以用这种方法
一达到6*6的时候,会超时,所以我们必须改进算法,分支限界算法

分支限界算法

分支定界 (branch and bound) 算法是一种在问题的解空间树上搜索问题的解的方法。但与回溯算法不同,分支定界算法采用广度优先或最小耗费优先的方法搜索解空间树,并且,在分支定界算法中,每一个活结点只有一次机会成为扩展结点。 
利用分支定界算法对问题的解空间树进行搜索,它的搜索策略是: 
1 .产生当前扩展结点的所有孩子结点; 
2 .在产生的孩子结点中,抛弃那些不可能产生可行解(或最优解)的结点; 
3 .将其余的孩子结点加入活结点表; 
4 .从活结点表中选择下一个活结点作为新的扩展结点。 
如此循环,直到找到问题的可行解(最优解)或活结点表为空。 
从活结点表中选择下一个活结点作为新的扩展结点,根据选择方式的不同,分支定界算法通常可以分为两种形式: 
1 . FIFO(First In First Out) 分支定界算法:按照先进先出原则选择下一个活结点作为扩展结点,即从活结点表中取出结点的顺序与加入结点的顺序相同。 
2 .最小耗费或最大收益分支定界算法:在这种情况下,每个结点都有一个耗费或收益。如果要查找一个具有最小耗费的解,那么要选择的下一个扩展结点就是活结点表中具有最小耗费的活结点;如果要查找一个具有最大收益的解,那么要选择的下一个扩展结点就是活结点表中具有最大收益的活结点。 
又称分支定界搜索法。过程系统综合的一类方法。该法是将原始问题分解,产生一组子问题。分支是将一组解分为几组子解,定界是建立这些子组解的目标函数的边界。如果某一子组的解在这些边界之外,就将这一子组舍弃(剪枝)。分支定界法原为运筹学中求解整数规划(或混合整数规划)问题的一种方法。用该法寻求整数最优解的效率很高。将该法原理用于过程系统综合可大大减少需要计算的方案数日。
分支定界法的思想是:首先确定目标值的上下界,边搜索边减掉搜索树的某些支,提高搜索效率。
在竞赛中,我们有时会碰到一些题目,它们既不能通过建立数学模型解决,又没有现成算法可以套用,或者非遍历所有状况才可以得出正确结果。这时,我们就必须采用搜索算法来解决问题。   
搜索算法按搜索的方式分有两类,一类是深度优先搜索,一类是广度优先搜索。我们知道,深度搜索编程简单,程序简洁易懂,空间需求也比较低,但是这种方法的时间复杂度往往是指数级的,倘若不加优化,其时间效率简直无法忍受;而广度优先搜索虽然时间复杂度比前者低一些,但其庞大的空间需求量又往往让人望而却步。   
所以,对程序进行优化,就成为搜索算法编程中最关键的一环。   
本文所要讨论的便是搜索算法中优化程序的一种基本方法枣“剪枝”。
什么是剪枝   
相信刚开始接触搜索算法的人,都做过类似迷宫这样的题目吧。我们在“走迷宫”的时候,一般回溯法思路是这样的:   
1、这个方向有路可走,我没走过   
2、往这个方向前进   
3、是死胡同,往回走,回到上一个路口   
4、重复第一步,直到找着出口   
这样的思路很好理解,编程起来也比较容易。但是当迷宫的规模很大时,回溯法的缺点便暴露无遗:搜索耗时极巨,无法忍受。   
我们可不可以在向某个方向前进时,先一步判断出这样走会不会走到死胡同里呢?这样一来,搜索的时间不就可以减少了吗?
答案是:可以的。     
剪枝的概念,其实就跟走迷宫避开死胡同差不多。若我们把搜索的过程看成是对一棵树的遍历,那么剪枝顾名思义,就是将树中的一些“死胡同”,不能到达我们需要的解的枝条“剪”掉,以减少搜索的时间。   
搜索算法,绝大部分需要用到剪枝。然而,不是所有的枝条都可以剪掉,这就需要通过设计出合理的判断方法,以决定某一分支的取舍。在设计判断方法的时候,需要遵循一定的原则。
剪枝的原则   
1、正确性   
正如上文所述,枝条不是爱剪就能剪的。如果随便剪枝,把带有最优解的那一分支也剪掉了的话,剪枝也就失去了意义。所以,剪枝的前提是一定要保证不丢失正确的结果。   
2、准确性   
在保证了正确性的基础上,我们应该根据具体问题具体分析,采用合适的判断手段,使不包含最优解的枝条尽可能多的被剪去,以达到程序“最优化”的目的。可以说,剪枝的准确性,是衡量一个优化算法好坏的标准。   
3、高效性   设计优化程序的根本目的,是要减少搜索的次数,使程序运行的时间减少。但为了使搜索次数尽可能的减少,我们又必须花工夫设计出一个准确性较高的优化算法,而当算法的准确性升高,其判断的次数必定增多,从而又导致耗时的增多,这便引出了矛盾。   
因此,如何在优化与效率之间寻找一个平衡点,使得程序的时间复杂度尽可能降低,同样是非常重要的。倘若一个剪枝的判断效果非常好,但是它却需要耗费大量的时间来判断、比较,结果整个程序运行起来也跟没有优化过的没什么区别,这样就太得不偿失了。   
综上所述,我们可以把剪枝优化的主要原则归结为六个字:正确、准确、高效。   
剪枝算法按照其判断思路可大致分成两类:可行性剪枝及最优性剪枝。
对于分支定界算法,上界是已求得的可行解的目标函数值中的最小者,分为初始上界和在探测过程中产生的动态上界.分支定界法在求最优解的迭代过程中, 若某结点估计的下界不小于已知的上界, 则不必从该节点往下继续搜索. 因此若能产生一个较好的上界, 可以消除许多不必要的列举计算.
分支定界算法的实现
在描述分支定界算法步骤之前, 先对算法涉及到的有关术语进行定义如下:
p —— 分支层数;
C*—— 当前最优目标函数值;
P*—— 相应于C*的工件顺序;
P1—— 当前节点(现在需要进行分支的节点)所对应的部分序列.
分支定界算法的实施步骤如下:
步骤1 初始化: 设置p = 0, P 1 = &Aacute; (空集) , C* = ∞.设当前节点总是与P 1 相对应. 此时, 当前节点即根节点.
步骤2 计算从当前节点分支得到的各个子节点的下界, 并按下界值由小到大对各子节点排序. 令p ←p + 1.
步骤3 如果当前节点被探测尽, 令p ←p - 1, 转步骤6. 否则, 设当前层(第p 层) 各活动子节点中具有最小下界值的节点为Q , 则在P 1末尾加入Q 对应第p 位置上的工件, 此时的当前节点转为Q , 转步骤4.
步骤4 因为当前节点是同层同父节点具有最小下界值的节点, 如果当前节点下界值大于或等于C* , 则不必再搜索当前节点及其同层同父的活动节点, 这样, 当前节点的上一层节点(父节点)被探测尽, p ←p - 1, 去掉P 1 中的最后一个工件,转步骤6. 否则, 转步骤5.
步骤5 如果p = n, 则得到一个较优顺序.令P* = P 1, C* 是当前节点的下界值, p ←p - 1,去掉P 1 中最后一个工件, 转步骤6; 否则转步骤2.
步骤6 若p ≠ 0, 去掉P 1 中最后一个工件,转步骤3; 否则, 算法停止. C* 是最优的目标函数值, P* 是最优顺序. 

-------------------------
限界条件:1.空格数大于等于要求的步数T;2.pd=t-已经走的步数-在最好的情况下当前位置到目标位置的步数,pd>=0,pd是偶数因为剩余步数-目标位置如果是奇数,那么在一个矩阵中就不可能走到目标位置
再考虑走过的路不能再走
只要在走下一步前把mg[j]设置已经走过,回到mg[j]的时候再把已经走过的标志消除就可以
--------------------
代码:

#include <stdio.h>
#include <math.h>
int m,n,t,flag,c,d,s;
char mg[8][8];
int zmg(int i,int j,int p)
{ int pd;
  if(s+1<t) return(0);
  if(flag) return(1);
  if(t==p&&i==c&&j==d) flag=1;
  pd=t-p-abs(i-c)-abs(j-d);
  if(pd<0||pd%2) return(0);
  if(mg[j]=='X') return(0);
  if(i==m||i<0||j==n||j<0) return(0);
  mg[j]='X';
  if(!flag) zmg(i-1,j,p+1);
  if(!flag) zmg(i+1,j,p+1);
  if(!flag) zmg(i,j-1,p+1);
  if(!flag) zmg(i,j+1,p+1);
  mg[j]='.';
  return(flag);
}
main()
{ int i,j,a,b;
  while(scanf("%d%d%d",&m,&n,&t)!=EOF&&m&&n&&t)
  { getchar();
    s=0;
    for(i=0;i<m;i++)
    { for(j=0;j<n;j++)
      { mg[j]=getchar();
        if(mg[j]=='S')
        { a=i,b=j;}
        if(mg[j]=='D')
        { c=i,d=j;}
        if(mg[j]=='.')
          s++;
      }
      getchar();
    }
    flag=0;
    if(zmg(a,b,0))
      printf("YES\n");
    else
      printf("NO\n");
  }
}

 

posted @ 2012-05-10 17:02  陈小怪  阅读(435)  评论(0编辑  收藏  举报