• [题解] PowerOJ 1749 孤岛营救问题 (最短路)


    - 传送门 -

     https://www.oj.swust.edu.cn/problem/show/1749

    # 1749: 孤岛营救问题

    Time Limit: 1000 MS Memory Limit: 65536 KB
    Total Submit: 50 Accepted: 29 Page View: 566

    Description

    大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的 地形图。迷宫的外形是一个长方形,其南北方向被划分为N 行,东西方向被划分为M列, 于是整个迷宫被划分为N×M 个单元。每一个单元的位置可用一个有序数对(单元的行号, 单元的列号)来表示。南北或东西方向相邻的2 个单元之间可能互通,也可能有一扇锁着的 门,或者是一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分成P类, 打开同一类的门的钥匙相同,不同类门的钥匙不同。 大兵瑞恩被关押在迷宫的东南角,即(N,M)单元里,并已经昏迷。迷宫只有一个入口, 在西北角。也就是说,麦克可以直接进入(1,1)单元。另外,麦克从一个单元移动到另一个 相邻单元的时间为1,拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。 编程任务: 试设计一个算法,帮助麦克以最快的方式到达瑞恩所在单元,营救大兵瑞恩。

    Input

    由文件input.txt提供输入数据。第1行有3个整数,分别表示N,M,P的值。第2 行是1 个整数K,表示迷宫中门和墙的总数。第I+2 行(1<=I<=K),有5 个整数,依次为 Xi1,Yi1,Xi2,Yi2,Gi: 当Gi>=1时,表示(Xi1,Yi1)单元与(Xi2,Yi2)单元之间有一扇第Gi类的门,当Gi=0 时, 表示(Xi1,Yi1)单元与(Xi2,Yi2)单元之间有一堵不可逾越的墙(其中,|Xi1-Xi2|+|Yi1-Yi2|=1, 0<=Gi<=P)。 第K+3行是一个整数S,表示迷宫中存放的钥匙总数。 第K+3+J 行(1<=J<=S),有3个整数,依次为Xi1,Yi1,Qi:表示第J 把钥匙存放在(Xi1,Yi1) 单元里,并且第J 把钥匙是用来开启第Qi类门的。(其中1<=Qi<=P)。 输入数据中同一行各相邻整数之间用一个空格分隔。

    Output

    程序运行结束时,将麦克营救到大兵瑞恩的最短时间的值输出到文件output.txt 中。如 果问题无解,则输出-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

    Source

    线性规划与网络流24题
     

    - 题意 -

     一个 n*m 的格点图.
     俩格点间可能无法通过, 可能需要特定钥匙.
     一些格点有一些钥匙.
     可以上下左右走一格, 时间为 1.
     求最短时间从((1,1))走到((n,m)).
     

    - 思路 -

     分层建模.
     以每一把钥匙的有无来分层(二进制表示).
     在每一个状态下, 枚举每一个节点看是否能到达它四周的节点,然后连边, (0) 状态下(没有一把钥匙)的((1,1))节点是起点, 任一状态下的((n, m))都是终点.
     跑最短路.
     细节见代码.
     
     PS:
     一个节点可能有多把钥匙.
     

    - 代码 -

    #include<cstdio>
    #include<queue>
    #include<algorithm>
    #include<cstring>
    using namespace std;
     
    const int N = 2050 * 100;
    const int M = N * 5;
    const int inf = 0x3f3f3f3f;
     
    int TO[M], V[M], NXT[M];
    int DIS[N], VIS[N], HD[N];
    int MAP[105][105], KY[105][105];
    int X[4] = {-1, 1, 0, 0}, Y[4] = {0, 0, -1, 1};
    int n, m, ss, tt, p, k, s, tot, sz;
    queue<int> q;
     
    int mk(int x, int y) {
        return (x - 1) * m + y;
    }
     
    int mk1(int p, int z) {
        return z * tot + p;
    }
     
    bool ok(int z, int k) {
        if (k == -1)
            return true;
        if (z & (1 << k))
            return true;
        return false;
    }
     
    int spfa() {
        memset(DIS, 0x3f, sizeof (DIS));
        DIS[ss] = 0;
        q.push(ss);
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            VIS[u] = 0;
            for (int i = HD[u]; i != -1; i = NXT[i]) {
                int v = TO[i];
                if (DIS[v] > DIS[u] + V[i]) {
                    DIS[v] = DIS[u] + V[i];
                    if (!VIS[v]) {
                        VIS[v] = 1;
                        q.push(v);
                    }
                }
            }
        }
        if (DIS[tt] == inf) return -1;
        return DIS[tt];
    }
     
    void add(int x, int y, int z) {
        TO[sz] = y; V[sz] = z;
        NXT[sz] = HD[x]; HD[x] = sz++;
    }
     
    int main() {
        memset(MAP, -1, sizeof (MAP));
        memset(HD, -1, sizeof (HD));
        scanf("%d%d%d%d", &n, &m, &p, &k);
        int x1, x2, y1, y2, tmp;
        tot = n * m;
        for (int i = 1; i <= k; ++i) {
            scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &tmp);
            int k1 = mk(x1, y1), k2 = mk(x2, y2);
            MAP[k1][k2] = MAP[k2][k1] = tmp - 1;
            if (tmp == 0)
                MAP[k1][k2] = MAP[k2][k1] = -2;
        }
        scanf("%d", &s);
        for (int i = 1; i <= s; ++i) {
            scanf("%d%d%d", &x1, &y1, &tmp);
            KY[x1][y1] |= 1 << (tmp - 1); //一个节点可能不止一把钥匙
        }
        tt = 204805;
        for (int i = 0; i < (1 << p); ++i) {
            for (int x = 1; x <= n; ++x) {
                for (int y = 1; y <= m; ++y) {
                    if (x == n && y == m)
                        add(mk1(mk(x, y), i), tt, 0); //设一个虚点做终点, 也可以最后枚举这些点.
                    int pt = mk(x, y);
                    for (int l = 0; l < 4; ++l) {
                        int z = i, xx = x + X[l], yy = y + Y[l];
                        if (xx > 0 && yy > 0 && xx <= n && yy <= m);
                        else continue;
                        if (MAP[mk(x, y)][mk(xx, yy)] == -2 || !ok(i, MAP[mk(x, y)][mk(xx, yy)])) //有墙或者没钥匙
                            continue;
                        if (KY[xx][yy])
                            z |= KY[xx][yy];
                        add(mk1(pt, i), mk1(mk(xx, yy), z), 1);
                    }
                }
            }
        }
        ss = mk1(mk(1, 1), 0);
        printf("%d
    ", spfa());
        return 0;
    }
    
  • 相关阅读:
    Java集合(4):Iterator(迭代器)
    Java集合(3):Vector && Stack
    Java集合(2):LinkedList
    面试题29:顺时针打印矩阵
    面试题28:对称的二叉树
    面试题27:二叉树的镜像
    面试题26:树的子结构
    面试题24:反转链表
    面试题25:合并两个排序的链表
    面试题23:链表中环的入口结点
  • 原文地址:https://www.cnblogs.com/Anding-16/p/7435266.html
Copyright © 2020-2023  润新知