仙岛求药 Dijkstra算法
题目描述
少年李逍遥的婶婶病了,王小虎介绍他去一趟仙灵岛,向仙女姐姐要仙丹救婶婶。叛逆但孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的深处。迷阵由M×N个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全。现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方格,而且那里会有神秘人物等待着他。现在要求你来帮助他实现这个目标。 下图 显示了一个迷阵的样例及李逍遥找到仙药的路线。
输入格式
输入有多组测试数据. 每组测试数据以两个非零整数 M 和 N 开始,两者均不大于20。M 表示迷阵行数, N 表示迷阵列数。接下来有 M 行, 每行包含N个字符,不同字符分别代表不同含义:
-
‘@’:少年李逍遥所在的位置;
-
‘.’:可以安全通行的方格;
-
‘#’:有怪物的方格;
-
‘*’:仙药所在位置。
当在一行中读入的是两个零时,表示输入结束。
输出格式
对于每组测试数据,分别输出一行,该行包含李逍遥找到仙药需要穿过的最少的方格数目(计数包括初始位置的方块)。如果他不可能找到仙药, 则输出 -1。
样例
样例输入
8 8
.@##…#
#…#.#
#.#.##…
…#.###.
#.#…#.
…###.#.
…#.…
.#…###
6 5
..#.
.#…
…##.
…
.#…
…@
9 6
.#…#.
.#.*.#
.####.
…#…
…#…
…#…
…#…
#.@.##
.#…#.
0 0
样例输出
10
8
-1
分析
首先正常人看到这道一定会想到用BFS,但我们要尝试阴间算法,我们不是正常人……
最短路
我们把这张n * m的地图看成n * m个点。不难发现,只要两个点之间可以以一步的距离走到,就可以连成一条线。那么再把这些点的最短步数即最短路算出来就行了。起点就是@(李逍遥的位置),重点就为*(仙药)。
初始化
首先读入这些点,再把这些点连接起来
假设这个点为n,m,则用hash(勉强算是吧)的思想把这个点记为M * (n - 1) + m,即可,方便后面访问该节点。
char A;
for(int i = 1; i <= N; i++) {
for(int j = 1; j <= M; j++) {
cin >> A;
if(A == '.')
mp[i][j] = 1;
else if(A == '#')
mp[i][j] = 2;
else if(A == '@') {
mp[i][j] = 3;
c1 = i; c2 = j;
}
else {
mp[i][j] = 4;
t1 = i; t2 = j;
}
}
}
c = M * (c1 - 1) + c2;
t = M * (t1 - 1) + t2;
for(int i = 1; i <= N; i++) {
for(int j = 1; j <= M; j++) {
int A = M * (i - 1) + j, B;
if(mp[i][j] == 0 || mp[i][j] == 2)
continue;
for(int k = 1; k <= 4; k++) {
int X = i + x[k], Y = j + y[k];
if(mp[X][Y] >= 1 && mp[X][Y] != 2) {
B = M * (X - 1) + Y;
a[A].push_back(Node(B, 1));
a[B].push_back(Node(A, 1));
}
}
}
}
核心代码
虽是核心代码,但是就是一个Dijkstra板子。
(不会吧不会吧,不会真的有人不会Dijkstra吧,不会真的有人用Floyed吧,这都什么年代了)
void Dijkstra() {
q.push(Node(c, 0));
memset(d, 0x3f, sizeof(d));
d[c] = 0;
while(!q.empty()) {
Node now = q.top();
q.pop();
int i = now.v;
if(f[i]) continue;
f[i] = 1;
int SIZ = a[i].size();
for(int j = 0; j < SIZ; j++) {
if(d[a[i][j].v] > d[i] + a[i][j].m) {
d[a[i][j].v] = d[i] + a[i][j].m;
q.push(Node(a[i][j].v, d[a[i][j].v]));
}
}
}
if(d[t] == 0x3f3f3f3f)
printf("-1\n");
else
printf("%d\n", d[t]);
}
代码
综上所述,代码就出来了……
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
const int MAXN = 1e3 + 5;
struct Node {
int v, m;
Node() {
}
Node(int V, int M) {
v = V;
m = M;
}
friend bool operator < (Node x, Node y) {
return x.m > y.m;
}
};
priority_queue<Node> q;
vector<Node> a[MAXN];
int mp[MAXN][MAXN];
int x[MAXN] = {
0, -1, 1, 0, 0};
int y[MAXN] = {
0, 0, 0, -1, 1};
int d[MAXN];
bool f[MAXN];
int N, M, n, m, c, t;
int c1, c2, t1, t2;
void Init() {
c1 = c2 = t1 = t2 = n = m = c = t = 0;
memset(d, 0, sizeof(d));
memset(f, 0, sizeof(f));
memset(mp, 0, sizeof(mp));
while(!q.empty())
q.pop();
for(int i = 0; i < MAXN; i++)
a[i].clear();
}
void Read() {
char A;
for(int i = 1; i <= N; i++) {
for(int j = 1; j <= M; j++) {
cin >> A;
if(A == '.')
mp[i][j] = 1;
else if(A == '#')
mp[i][j] = 2;
else if(A == '@') {
mp[i][j] = 3;
c1 = i; c2 = j;
}
else {
mp[i][j] = 4;
t1 = i; t2 = j;
}
}
}
c = M * (c1 - 1) + c2;
t = M * (t1 - 1) + t2;
for(int i = 1; i <= N; i++) {
for(int j = 1; j <= M; j++) {
int A = M * (i - 1) + j, B;
if(mp[i][j] == 0 || mp[i][j] == 2)
continue;
for(int k = 1; k <= 4; k++) {
int X = i + x[k], Y = j + y[k];
if(mp[X][Y] >= 1 && mp[X][Y] != 2) {
B = M * (X - 1) + Y;
a[A].push_back(Node(B, 1));
a[B].push_back(Node(A, 1));
}
}
}
}
q.push(Node(c, 0));
}
void Dijkstra() {
memset(d, 0x3f, sizeof(d));
d[c] = 0;
while(!q.empty()) {
Node now = q.top();
q.pop();
int i = now.v;
if(f[i]) continue;
f[i] = 1;
int SIZ = a[i].size();
for(int j = 0; j < SIZ; j++) {
if(d[a[i][j].v] > d[i] + a[i][j].m) {
d[a[i][j].v] = d[i] + a[i][j].m;
q.push(Node(a[i][j].v, d[a[i][j].v]));
}
}
}
if(d[t] == 0x3f3f3f3f)
printf("-1\n");
else
printf("%d\n", d[t]);
}
int main() {
while(scanf("%d %d", &N, &M) != EOF && N && M) {
Init();
Read();
Dijkstra();
}
return 0;
}
这个算法时间复杂度为 O ( n ∗ m ∗ l o g ( n ∗ m ) ) O(n*m*log(n*m)) O(n∗m∗log(n∗m)),比那啥bfs说不定就快多了。
这就是阴间的代码吗,i了i了