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

来源:互联网 发布:python http get请求 编辑:程序博客网 时间:2024/06/06 01:00

题目链接


题意:

给定 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 110using namespace std;struct 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 110using 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;}



原创粉丝点击