[题解] 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\n", spfa());
    return 0;
}
posted @ 2017-08-26 14:26  lstttt  阅读(638)  评论(0编辑  收藏  举报