1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图。迷宫的外形是一个长方形,其南北方向被划分为 n 行,东西方向被划分为 m 列, 于是整个迷宫被划分为 n×m 个单元。每一个单元的位置可用一个有序数对 (单元的行号, 单元的列号) 来表示。南北或东西方向相邻的 2 个单元之间可能互通,也可能有一扇锁着的门,或者是一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分成 p 类, 打开同一类的门的钥匙相同,不同类门的钥匙不同。
大兵瑞恩被关押在迷宫的东南角,即 (n,m) 单元里,并已经昏迷。迷宫只有一个入口, 在西北角。也就是说,麦克可以直接进入 (1,1) 单元。另外,麦克从一个单元移动到另一个 相邻单元的时间为 1,拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。
试设计一个算法,帮助麦克以最快的方式到达瑞恩所在单元,营救大兵瑞恩。
第一行有三个整数,分别表示 n,m,p 的值。
第二行是一个整数k,表示迷宫中门和墙的总数。
第 i+2行 (1≤i≤k),有 5 个整数,依次为 xi1,yi1,xi2,gi:当 gi≥1时,表示 (xi1,yi1) 单元与 (xi2,yi2) 单元之间有一扇第 gi 类的门,当 gi=0时, 表示 (xi1,yi1) 单元与 (xi2,yi2) 单元之间有一堵不可逾越的墙。
第 k+3 行是一个整数 s,表示迷宫中存放的钥匙总数。
第 k+3+j行 (1≤j≤s),有 3 个整数,依次为 xi1,yi1,qi,表示第 j 把钥匙存放在 (xi1,yi1) 单元里,并且第 j 把钥匙是用来开启第 qi 类门。
输入数据中同一行各相邻整数之间用一个空格分隔。
输出麦克营救到大兵瑞恩的最短时间。如果问题无解,则输出 −1。
4 4 9
9
1 2 1 3 2
1 2 2 2 0
2 1 2 2 0
2 1 3 1 0
2 3 3 3 0
2 4 3 4 1
3 2 3 3 0
3 3 4 3 0
4 3 4 4 0
2
2 1 2
4 2 1
14
【数据范围与提示】
基本广搜
#include
#include
#include
#define SIZE 20
using namespace std;
struct node
{
int x, y, step;
bool key[SIZE];
};
// 12维数组
queue q;
int way[SIZE][SIZE][SIZE][SIZE], n, m, p;
bool key[SIZE][SIZE][2][2][2][2][2][2][2][2][2][2], visited[SIZE][SIZE], havekey[SIZE][SIZE][SIZE];
int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};
bool check(int x, int y, int r, int c, bool havekey[]) // 判断能不能走
{
int temp = way[x][y][r][c], i;
if (((!r) || (r > n)) || ((!c) || (c > m)))
{
return false;
}
if (!temp)
{
return false;
}
if (~temp)
{
if (!havekey[temp])
{
return false;
}
}
bool &f = key[r][c][havekey[1]][havekey[2]][havekey[3]][havekey[4]][havekey[5]][havekey[6]][havekey[7]][havekey[8]][havekey[9]][havekey[10]]; // 很暴力
if (f)
{
return false;
}
visited[r][c] = true;
f = true;
return true;
}
int main(void)
{
int k, i, j, x, y, r, c;
bool a[11] = {};
memset(way, -1, sizeof (way));
scanf("%d%d%d%d", &n, &m, &p, &k);
while (k--)
{
scanf("%d%d%d%d%d", &x, &y, &r, &c, &i);
way[r][c][x][y] = way[x][y][r][c] = i;
}
scanf("%d", &k);
while (k--)
{
scanf("%d%d%d", &x, &y, &i);
havekey[x][y][i] = true; // 一个格子可能有多个钥匙
}
visited[1][1] = key[1][1][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]][a[9]][a[10]] = true; // 很暴力
q.push({1, 1, 0, {}});
for (i = 1; i <= p; ++i)
{
if (havekey[1][1][i])
{
q.front().key[i] = true;
}
}
while (!q.empty()) // 基本广搜
{
x = q.front().x;
y = q.front().y;
for (i = 0; i < 4; ++i)
{
r = x + dx[i];
c = y + dy[i];
if (check(x, y, r, c, q.front().key))
{
if ((r == n) && (c == m)) // 到达终点,输出最短路径
{
printf("%d", q.front().step + 1);
return 0;
}
q.push({r, c, q.front().step + 1, {}});
for (j = 1; j <= p; ++j)
{
if ((q.front().key[j]) || (havekey[r][c][j]))
{
q.back().key[j] = true;
}
}
}
}
q.pop();
}
printf("-1"); // 到不了,输出-1
return 0;
}