HDOJ1728 逃离迷宫 ---- DFS+剪枝

题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1728

Problem Description

  给定一个m × n (m行, n列)的迷宫,迷宫中有两个位置,gloria想从迷宫的一个位置走到另外一个位置,当然迷宫中有些地方是空地,gloria可以穿越,有些地方是障碍,她必须绕行,从迷宫的一个位置,只能走到与它相邻的4个位置中,当然在行走过程中,gloria不能走到迷宫外面去。令人头痛的是,gloria是个没什么方向感的人,因此,她在行走过程中,不能转太多弯了,否则她会晕倒的。我们假定给定的两个位置都是空地,初始时,gloria所面向的方向未定,她可以选择4个方向的任何一个出发,而不算成一次转弯。gloria能从一个位置走到另外一个位置吗?

 

 

Input

  第1行为一个整数t (1 ≤ t ≤ 100),表示测试数据的个数,接下来为t组测试数据,每组测试数据中,
  第1行为两个整数m, n (1 ≤ m, n ≤ 100),分别表示迷宫的行数和列数,接下来m行,每行包括n个字符,其中字符'.'表示该位置为空地,字符'*'表示该位置为障碍,输入数据中只有这两种字符,每组测试数据的最后一行为5个整数k, x1, y1, x2, y2 (1 ≤ k ≤ 10, 1 ≤ x1, x2 ≤ n, 1 ≤ y1, y2 ≤ m),其中k表示gloria最多能转的弯数,(x1, y1), (x2, y2)表示两个位置,其中x1,x2对应列,y1, y2对应行。

 

 

Output

  每组测试数据对应为一行,若gloria能从一个位置走到另外一个位置,输出“yes”,否则输出“no”。

 

 

Sample Input

 

2 5 5 ...** *.**. ..... ..... *.... 1 1 1 1 3 5 5 ...** *.**. ..... ..... *.... 2 1 1 1 3

 

 

Sample Output

 

no yes

 

 题解:可以用DFS搜索所有情况判断是否有满足题目条件的。定义一个结构体Node,成员变量有x,y表示行列坐标,dir表示方向,count表示转弯次数。bool dfs(Node node)就表示满足的node节点能否走到终点。

从起点开始判断,只需要四次循环,走上下左右四个方向,任何一个方向dfs返回true就说明可以走到终点,否则不能。

每次dfs一个节点时,需要先判断是否是墙,是否越界,转弯次数是否超标。最重要的是需要剪枝,如果dfs到某个node时,之前可能已经搜过该node了,就需要判断是否需要剪枝,如果判断出之前搜过该点,并且之前搜该店时最小的转弯次数比本次搜该点的转弯次数小(有点绕),那么现在去搜这个点肯定肯定走不到终点,所以需要直接return false来剪枝,没有这步剪枝应该会TLE。

 为了容易看明白些,代码有些可以省略简写的地方没有省略,比如搜4个方向可以for循环,这里就直接贴了4遍代码。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define TOP 0
#define BOTTOM 1
#define LEFT 2
#define RIGHT 3
using namespace std;
int m,n;   // 地图行列数
int Max;    // 最多转弯次数
char Map[105][105]; // 存放地图,从1开始
// 避免走回头路,要满足该点visited且转弯次数更大
bool visited[105][105];     //每个点访问记录
int visitedCount[105][105];     // 记录每个点访问时的转弯次数
struct Node {
    int x,y;        // 位置,x:行,y:列
    int dir;        // 方向
    int count;      // 转弯次数
    Node() {
        dir = count = x = y = 0;
    }
}start,End;             // 起始点和目标点

/**
 * 深度优先搜索
 */
bool dfs(Node node) {
    int x = node.x; //行
    int y = node.y; //列
    if (Map[x][y] == '*')
        return false;
    if (visited[x][y]) {
        if (visitedCount[x][y] < node.count) {
            // 走了回头路且肯定会失败
            return false;    
        } else {
            // 更新转弯次数
            visitedCount[x][y] = node.count;
        }
        
    }
    if (x < 1 || y < 1 || x > m || y > n) {
        //越界
        return false;
    }
    if (node.count > Max) {
        // 转弯次数超出限制
        return false;
    }
    if (x == End.x && y == End.y) {
        // 达到终点
        return true;
    }
    /*
     * 该点可以走
     */
    visited[x][y] = true;
    visitedCount[x][y] = node.count;
    Node temp;
    // 走四个方向
    // 上
    temp.x = x - 1,temp.y = y,temp.dir = TOP;
    temp.count = (node.dir == TOP ? node.count : node.count+1);
    // 起点特判
    if (node.x == start.x && node.y == start.y)
        temp.count = 0;
    if (dfs(temp) == true)
        return true;
    // 下
    temp.x = x + 1,temp.y = y,temp.dir = BOTTOM;
    temp.count = (node.dir == BOTTOM ? node.count : node.count+1);
    if (node.x == start.x && node.y == start.y)
        temp.count = 0;
    if (dfs(temp) == true)
        return true;
    // 左
    temp.x = x,temp.y = y - 1,temp.dir = LEFT;
    temp.count = (node.dir == LEFT ? node.count : node.count+1);
    if (node.x == start.x && node.y == start.y)
        temp.count = 0;
    if (dfs(temp) == true)
        return true;
    // 右
    temp.x = x,temp.y = y + 1,temp.dir = RIGHT;
    temp.count = (node.dir == RIGHT ? node.count : node.count+1);
    if (node.x == start.x && node.y == start.y)
        temp.count = 0;
    if (dfs(temp) == true)
        return true;
        
    return false;    
}


int main() {
    int t;
    scanf("%d",&t);
    while(t--) {
        memset(visitedCount,0,sizeof(visitedCount));
        memset(visited,0,sizeof(visited));
        scanf("%d%d",&m,&n);
        for (int i = 1;i <= m;i++)
            scanf("%s",Map[i] + 1);
        scanf("%d",&Max);
        scanf("%d%d%d%d",&start.y,&start.x,&End.y,&End.x);
        start.count = 0;
        if (dfs(start)) {
            printf("yes\n");
        } else {
            printf("no\n");
        }
    }
    
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值