您的位置:首页 > 其它

hdu 1728 逃离迷宫 普通的队列 / 优先队列

2017-07-21 00:06 225 查看
题目链接

题意:

给定 m * n 的矩阵与起点终点位置, 限制转弯次数 k, 问能否到达终点

是个很显然的bfs, 但是

1. 要注意的是转弯无论一次转几个方向都是转一次, 一开始想当然的认为如果从(1, 0)方向转向(0, 1)方向就是转了两次, 结果一直WA...

2. 大概是因为是pascal入门所以一直对优先队列没什么概念, 而一直老老实实写普通的队列...但感觉大家都很直接的知道就是应该写优先队列

下面就来说一下两种不同的写法吧

(觉得优先队列写起来真是方便Orz

1. 

如果是写一般的队列, 要注意用 num[i][j][dir] 来记录以方向 dir 来访问 map[i][j] 时用的最少步数, 

不能直接开个 vis[i][j] 来记录是否访问过这个格子; 即使开了 vis[i][j][dir] 记录了是否以特定方向访问过某个格子, 也无法保证是最少的步数.

当且仅当步数少于 num[i][j][dir] 时将这个 node 给 push 进队列, 并且更新 num[i][j][dir].

从队头取出结点时, 如果其转弯数 num > num[i][j][dir] (即意味着在队列中的后面有更少的步数), 那就对其不作处理, 直接 pop 出去.

思路还是挺清楚的.

此外, 因为一直不能保证当前的 num 为最小值 (至少在我一开始以为一次性可以转两次的情况下...), 所以需要跑完整张图, 但是100 * 100还是很小的, 也没关系

AC代码如下:

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#include <cmath>
#define inf 0x3f3f3f3f
#define maxn 110
using namespace std;
str
ea6c
uct node {
int x, y, num, dir;
};
queue<node> q;
const int dx[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
int num[maxn][maxn][4], a[maxn][maxn], m, n;
char s[maxn][maxn];
inline min(int a, int b) { return a < b ? a : b; }
void work() {
for (int i = 1; i <= m; ++i) {
scanf("%s", s[i]);
}
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (s[i][j - 1] == '.') a[i][j] = 0;
else a[i][j] = 1;
}
}
int k, x1, y1, x2, y2;
scanf("%d%d%d%d%d", &k, &y1, &x1, &y2, &x2);
memset(num, -1, sizeof(num));
for (int i = 0; i < 4; ++i) {
num[x1][y1][i] = 0;
int tx = x1 + dx[i][0], ty = y1 + dx[i][1];
if (tx > 0 && tx <= m && ty > 0 && ty <= n && !a[tx][ty]) {
node temp;
temp.x = tx; temp.y = ty; temp.num = 0; temp.dir = i;
num[tx][ty][i] = 0;
q.push(temp);
}
}

while (!q.empty()) {
node nowf = q.front();
int x = nowf.x, y = nowf.y, nnum = nowf.num, d = nowf.dir;
// printf("%d %d\n", x, y);
if (nnum <= num[x][y][d]) {
for (int i = 0; i < 4; ++i) {
int tx = x + dx[i][0], ty = y + dx[i][1];
if (tx > 0 && tx <= m && ty > 0 && ty <= n && !a[tx][ty]) {
int tot;
if (d == i) tot = nnum;
else tot = nnum + 1;
// int tot = min(abs(i - d), abs(4 - (i - d))) + nnum;
if (num[tx][ty][i] == -1 || tot < num[tx][ty][i]) {
num[tx][ty][i] = tot;
node temp;
temp.x = tx; temp.y = ty; temp.num = tot; temp.dir = i;
q.push(temp);
}
}
}
}
q.pop();
}
int ans = inf;

for (int i = 0; i < 4; ++i) if (num[x2][y2][i] != -1) ans = min(ans, num[x2][y2][i]);
if (ans <= k) printf("yes\n");
else printf("no\n");
// printf("%d\n", ans);
}
int main() {
// freopen("1728.in", "r", stdin);
int t;
scanf("%d", &t);
while (scanf("%d%d\n", &m, &n) != EOF) work();
return 0;
}


2.

优先队列的话就可以保证一点, 那就是,

在下一次以这个方向访问这个节点时, 其值必然大于等于上一次的值,

故在取出队头时, 一旦其为终点, 必然就是转弯次数最少的结果, 就可以结束了.

因此, 有一点要注意, 就是跑下一组数据时, 必须要先 while (!q.empty()) q.pop();

并且此时就可以放心地用 vis[i][j][dir] 来表示是否以这个方向访问过这个位置了, 因为第一次访问必然是最优的访问

写起来也很好写 (挺短

AC代码如下:

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#include <cmath>
#define inf 0x3f3f3f3f
#define maxn 110
using namespace std;
struct node {
int x, y, num, dir;
node(int xx = 0, int yy = 0, int nn = 0, int dd = 0) : x(xx), y(yy), num(nn), dir(dd) {}
bool operator < (const node& b) const { return num > b.num; }
};
priority_queue<node> q;
const int dx[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
int a[maxn][maxn], m, n;
bool vis[maxn][maxn][4];
char s[maxn][maxn];
inline min(int a, int b) { return a < b ? a : b; }
void work() {
for (int i = 1; i <= m; ++i) {
scanf("%s", s[i]);
}
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (s[i][j - 1] == '.') a[i][j] = 0;
else a[i][j] = 1;
}
}
memset(vis, 0, sizeof(vis));
int k, x1, y1, x2, y2;
scanf("%d%d%d%d%d", &k, &y1, &x1, &y2, &x2);
while (!q.empty()) q.pop();
for (int i = 0; i < 4; ++i) q.push(node(x1, y1, 0, i));
while (!q.empty()) {
node nowf = q.top(); q.pop();
int x = nowf.x, y = nowf.y, num = nowf.num, d = nowf.dir;
// printf("%d %d %d\n", x, y, num);
if (num > k) { printf("no\n"); return; }
if (x == x2 && y == y2) { printf("yes\n"); return; }
if (vis[x][y][d]) continue;
vis[x][y][d] = true;
for (int i = 0; i < 4; ++i) {
int tx = x + dx[i][0], ty = y + dx[i][1];
if (tx > 0 && tx <= m && ty > 0 && ty <= n && !a[tx][ty] && !vis[tx][ty][d]) {
int tot = num;
if (d != i) ++tot;
q.push(node(tx, ty, tot, i));
}
}
}
printf("no\n");
}
int main() {
// freopen("1728.in", "r", stdin);
int t;
scanf("%d", &t);
while (scanf("%d%d\n", &m, &n) != EOF) work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: