Hdu 1010 Tempter of the Bone

经典深搜加剪枝问题!问题主要是奇偶剪枝这地方卡了许久,步数剪枝也没考虑到,还有对DFS于BFS的区别了解不多。
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1010

存在的问题:1、对深搜和广搜的理解不深刻,特别是回溯的状态与条件。2、实现一个算法的速度还比较慢! 3、以后少花时间去追程序的BUG,多花时间去想想算法!

剪枝前后对比:
第一个是删掉奇偶剪枝后的情况: 
int temp=T - t - abs(x - n) - abs(y - m);             //奇偶剪枝 
if(temp < 0 || temp & 1)  return ; 
第二个是删掉步数剪枝后的情况:
if(T > empty  )  { printf("NO\n"); continue; } 

奇偶剪枝的原理:

任意时刻,abs(ex-x)+abs(ey-y)都表示当前点p和逃生点ep之间的二维距离,可以证明,这时当前点p到逃生点ep之间的最短距离!
记此最短距离长度为s。如果,此最短距离上有一些障碍物不能走,那么移动会偏移最短距离s,但是不管偏移几个点,偏移的距离都是最短距离s加上一个偶数距离,这也是可以证明的!
简单的举个例子:
S...                                   
....                        
....                                   
...D                                   
最短距离s:
S... 
|...
|...
+--D 
假如有偏移距离s':
S...
+-+. 
+-+.
+--D
上面的图,线条代表路径,加号代表路径中的拐点。原2D最短路径是s,偏移后距离为s+4。你自己多画些图试试,那么,你发现偏离最短路径导致增加的长度其实只会是偶数!

所以,如果剩余时间减去最短路径s,得到一个奇数,那么显然是无法在t时刻准时到达逃生门ep的!这种情况下,当然不在搜索,直接返回No,以节省时间!

 

 

 

可以把地图 a 看成这样: 
0 1 0 1 0 1 
1 0 1 0 1 0 
0 1 0 1 0 1 
1 0 1 0 1 0 
0 1 0 1 0 1 
从为 0 的格子走一步,必然走向为 1 的格子 
从为 1 的格子走一步,必然走向为 0 的格子 
即: 
 0 ->1或1->0 必然是奇数步 
 0->0 走1->1 必然是偶数步 
结论:
所以当遇到从 0 走向 0 但是要求时间是奇数的,或者, 从 1 走向 0 但是要求时间是偶数的 都可以直接判断不可达!
某一时刻,狗到达某一位置,x1,y1,那么到达终点至少还要ex-x1+ey-y1 因为不能在格子上停留,且不能重复走过的格子,所以要求剩余时间和所需时间 同奇,或同偶,所以他们相减应该得到偶数,如果得到奇数,那么可以直接判断没办法到达了 , 所以你上面的if中式子是判断剩余的时间是否为偶数,为奇数就不能到达了
   所以它的作用是剪枝啦,剪掉不必要的搜索,否则会超时! 
----------------------------------------------------------------
http://zhidao.baidu.com/question/172913731.html
 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include< string.h>
 4  #define MAX_LEN 1
 5 
 6  char maze[MAX_LEN +  10][MAX_LEN +  10];
 7  const  int move[ 4][ 2]={{ 01},{ 10},{ 0, - 1},{- 10}};
 8  int flag[MAX_LEN +  10][MAX_LEN +  10];
 9 
10  int n , m;
11  int N , M , T;
12  int r , c;
13  int escape;
14  int empty;
15 
16 
17  bool Check( int x ,  int y)
18 {
19      if(!flag[x][y] && maze[x][y] !=  ' X ' && x >=  0 && x < N && y >=  0&& y < M ) 
20          return  1;
21       return  0;
22 }
23 
24 
25 
26  void dfs( int x ,  int y,  int t)
27 {
28      if(escape)  return ;           //flag[x][y] = 1的话使得原来的点只能走一次,而不能回溯了!
29      int tmp = T - t - abs(x - n) - abs(y - m);

30     if(tmp < 0 || tmp & 1return ;             
31     if(x == n && y == m && t == T)  {    escape = 1return ; }
32     for(int i = 0; i < 4; i++)
33     {
34         int xx = x + move[i][0];
35         int yy = y + move[i][1];
36         if(Check(xx , yy))
37         {
38          flag[xx][yy] = 1;                                   //标记的记录
39          dfs(xx , yy , t+1);
40          flag[xx][yy] = 0;                                   //回溯
41         }    
42     }
43       return ;
44 }
45 
46 
47 
48 int main()
49 {
50     while(~scanf("%d%d%d",&N , &M , &T ) , N , M , T)
51     {
52         int i,j;
53         
54         escape = empty = 0;
55         
56         for(i = 0;i < N; i++)    //当时是用scanf("%c",&maze[i][j]),相当麻烦!~
57         {
58             scanf("%s", maze[i]);
59             getchar();
60             for(j = 0;j < M; j++)
61             {
62                 if(maze[i][j] == 'S') {r = i; c = j;}
63                 if(maze[i][j] == 'D') { n = i; m = j;  }
64                 if(maze[i][j] == '.') {    empty ++;}              //步数剪枝
65             }
66         }
67         
68         
69         memset(flag , 0 , sizeof(flag));
70         
71         if(T > empty+1)     {    printf("NO\n"); continue; }
72         flag[r][c] = 1;          //起点标记 
73         dfs ( r , c , 0);
74         
75         if(escape) printf("YES\n");
76         else printf("NO\n");
77     }
78     return 0;

79 } 

 

你可能感兴趣的:(HDU)