hdu 1728 DFS+剪枝 逃离迷宫

 

 

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 
  5 typedef struct Node
  6 {
  7     int x, y;
  8 }Node;
  9 
 10 const int MAX = 10000;
 11 const int N = 110;
 12 const int dir[4][2] = { {-1,0}, {0,1}, {1,0}, {0,-1} };//移动方向
 13 bool visit[N][N];        //标记数组
 14 char board[N][N];        //棋盘
 15 int turn[N][N];          //turn数组用于记录棋盘上各点已知的最少的转弯次数
 16 Node origin, destination;//起点、终点
 17 int maxTurnCount;        //路痴所能承受的最大转弯次数
 18 int m, n;                //棋盘的行、列
 19 
 20 
 21 inline bool inBoard(const int &row, const int &col)
 22 {
 23     return 0 <= row && row < m && 0 <= col && col < n;
 24 }
 25 
 26 /* dfs搜索(参数含义:行、列、上一次移动的方向) */
 27 bool dfs(const int &row, const int &col, int direction)
 28 {
 29     //在路痴可以承受的转向次数内到达终点
 30     if (row == destination.x && col == destination.y && turn[row][col] <= maxTurnCount)
 31         return true;
 32 
 33     //转向的次数超过路痴所能承受的次数,路痴已经晕了,因此不可取
 34     if (turn[row][col] > maxTurnCount)
 35         return false;
 36 
 37     //路痴的位置与终点不在同一行同一列,那么至少需要再转向一次,但是当前的转向次数已经等于路痴所能承受的最大转弯次数
 38     if (row != destination.x && col != destination.y && turn[row][col] == maxTurnCount)
 39         return false;
 40 
 41     for (int i = 0; i < 4; ++i)
 42     {
 43         int r = row + dir[i][0];
 44         int c = col + dir[i][1];
 45         if (true == inBoard(r, c) && '.' == board[r][c] && false == visit[r][c])
 46         {
 47             /* turn[r][c]记录的是历史上走到(r,c)所用的最短的转弯次数,turn[r][c]的初始值MAX */
 48             /* 剪枝1:从(row, col)位置走向(r, c)位置,如果turn[r][c] < turn[row][col],说明之前
 49             路痴已经走到过这里了,并且上次的转弯次数比这次小,上次都没走到终点,这次更加走不到。
 50             相等的情况不能剪掉,举个栗子:从起点(1, 1),走向终点(3, 5),一种方法是向右走,一种方法
 51             是向下走。向右走的时候,走到(3, 4)的时候,转弯次数是1,那么turn[3][4] = 1,在转一次
 52             才走到,那么需要转两次;向下走的时候,又走到(3, 4),转弯次数也是1,由于之前走到过,
 53             然而两次转弯次数相同,后一种情况(走到终点只需要转弯一次)被剪枝剪掉了,如果题目给
 54             出的最大转弯次数是1,那么就得不到正确答案了。
 55             ....*
 56             .**.*
 57             .....
 58             */
 59             if (turn[r][c] < turn[row][col])
 60                 continue;
 61 
 62             /* 剪枝2:跟剪枝1的原理类似,从(row, col)走向(r, c),由于方向改变了,因此转弯次数
 63             需要加一,转弯次数加一后如果比(r, c)历史上的转弯次数还多,就需要把这种情况给剪掉。
 64             相等的情况不能剪掉 */
 65             if (-1 != direction && i != direction && turn[r][c] < turn[row][col] + 1)
 66                 continue;
 67 
 68             if (-1 != direction && i != direction)
 69                 turn[r][c] = turn[row][col] + 1;//移动方向与之前的不同,转弯次数+1
 70             else
 71                 turn[r][c] = turn[row][col];    //移动方向与之前的相同,转弯次数不变
 72 
 73             visit[r][c] = true;
 74 
 75             if (true == dfs(r, c, i)) return true;
 76 
 77             //回溯
 78             visit[r][c] = false;
 79         }
 80     }
 81     return false;
 82 }
 83 
 84 void input(void)
 85 {
 86     scanf("%d %d", &m, &n);     //输入行、列
 87     for (int i = 0; i < m; ++i) //输入棋盘
 88         scanf("%s", board[i]);
 89 
 90     //这里有坑:“5个整数k, x1, y1, x2, y2 (1 ≤ k ≤ 10, 1 ≤ x1, x2 ≤ n, 1 ≤ y1, y2 ≤ m)”
 91     //1 ≤ x1, x2 ≤ n  => x1,x2是列;1 ≤ y1, y2 ≤ m  => y1,y2是行
 92     //出题人挖的坑,不要被惯性思维束缚了
 93     scanf("%d %d %d %d %d", &maxTurnCount, &origin.y, &origin.x, &destination.y, &destination.x);
 94 
 95     //初始化数据
 96     for (int i = 0; i < m; ++i)
 97         for (int j = 0; j < n; ++j)
 98             turn[i][j] = MAX;
 99     memset(visit, 0, sizeof(visit));
100     origin.y--; //题目给出的坐标从1开始,因此需要对坐标进行处理,让其从0开始
101     origin.x--;
102     destination.y--;
103     destination.x--;
104 }
105 
106 int main(void)
107 {
108     int t;//测试组数
109     scanf("%d", &t);
110     for (int k = 0; k < t; ++k)
111     {
112         input();
113         visit[origin.x][origin.y] = true;
114         turn[origin.x][origin.y] = 0;
115         if (true == dfs(origin.x, origin.y, -1))
116             printf("yes\n");
117         else
118             printf("no\n");
119     }
120     return 0;
121 }

 

posted on 2016-07-21 20:34  yqzheng  阅读(248)  评论(0编辑  收藏  举报

导航