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;
}
上一篇: POJ 迷宫问题(经典BFS问题)
下一篇: Dijkstra--vector实现