Acwing 1131.拯救大兵瑞恩(拆点)

简介: 笔记

AcWing 1131. 拯救大兵瑞恩

1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。


瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图。


迷宫的外形是一个长方形,其南北方向被划分为 N 行,东西方向被划分为 M 列, 于是整个迷宫被划分为 N×M 个单元。


每一个单元的位置可用一个有序数对 (单元的行号, 单元的列号) 来表示。


南北或东西方向相邻的 2 个单元之间可能互通,也可能有一扇锁着的门,或者是一堵不可逾越的墙。


注意: 门可以从两个方向穿过,即可以看成一条无向边。


迷宫中有一些单元存放着钥匙,同一个单元可能存放 多把钥匙,并且所有的门被分成 P 类,打开同一类的门的钥匙相同,不同类门的钥匙不同。


大兵瑞恩被关押在迷宫的东南角,即 (N,M) 单元里,并已经昏迷。


迷宫只有一个入口,在西北角。


也就是说,麦克可以直接进入 (1,1)单元。


另外,麦克从一个单元移动到另一个相邻单元的时间为 1,拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。


试设计一个算法,帮助麦克以最快的方式到达瑞恩所在单元,营救大兵瑞恩。


输入格式

第一行有三个整数,分别表示 N,M,P 的值。


第二行是一个整数 k,表示迷宫中门和墙的总数。

16.png

输出格式

输出麦克营救到大兵瑞恩的最短时间。

如果问题无解,则输出 -1。


数据范围


17.png


思路

分层图最短路。将每个点划分为几个状态,状态的划分由走到当前点拥有哪些钥匙决定。


d[i][j] 表示走到 i 这个点且当前拥有钥匙的状态为 j 的最短距离。


每次bfs 到一个新的点时,判断点之间是否有门, 如果有的话是否有钥匙,然后如果能通过的话,更新邻点的最短距离。


如果当前走到的点有钥匙因为捡钥匙代价为0,所以能捡就捡,并且更新下拥有的钥匙的状态。


代码

// Author:zzqwtcc
// Problem: C - Swiss-System Tournament
// Contest: AtCoder - Exawizards Programming Contest 2021(AtCoder Beginner Contest 222)
// Time:2021-10-09 20:00:14
// URL: https://atcoder.jp/contests/abc222/tasks/abc222_c
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
#include<bits/stdc++.h>
#include<unordered_map>
// #define int long long
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3f
#define mod 1000000007
#define MOD 998244353
#define rep(i, st, ed) for (int (i) = (st); (i) <= (ed);++(i))
#define pre(i, ed, st) for (int (i) = (ed); (i) >= (st);--(i))
#define debug(x,y) cerr << (x) << " == " << (y) << endl;
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<typename T> inline T lowbit(T x) { return x & -x; }
// template<typename T> T qmi(T a, T b = mod - 2, T p = mod) { T res = 1; b %= (p - 1 == 0 ? p : p - 1); while (b) { if (b & 1) { res = (LL)res * a % p; }b >>= 1; a = (LL)a * a % p; }return res % mod; }
const int N = 90 + 10;
int n, m, p;
int mp[20][20];
vector<PII>vec[110];
set<PII>edges;
int key[105];
bool vis[105][1 << 10 + 1];
int d[105][1 << 10 + 1];
int dx[] = { 0,1,0,-1 }, dy[] = { 1,0,-1,0 };
void build() {
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            for (int k = 0; k < 4; ++k) {
                int x = i + dx[k];
                int y = j + dy[k];
                if (!x || x > n || !y || y > m)continue;
                if (edges.count({ mp[i][j],mp[x][y] }) == 0) {
                    vec[mp[i][j]].push_back({ mp[x][y],0 });
                }
            }
        }
    }
}
int bfs() {
    deque<PII>q;
    q.push_back({ 1,0 });
    memset(vis, 0, sizeof vis);
    memset(d, 0x3f, sizeof d);
    d[1][0] = 0;
    while (q.size()) {
        auto t = q.front();
        q.pop_front();
        if (vis[t.first][t.second])continue;
        vis[t.first][t.second] = true;
        //因为是 bfs 所以第一次遍历到当前点就是其最短距离
        if (t.first == n * m)return d[t.first][t.second];
        // 如果当前点有钥匙,因为捡钥匙是没有花费的,所以能捡就捡
        if (key[t.first]) {
            // 更新拥有钥匙的状态
            int state = t.second | key[t.first];
            // 更新新状态的最短距离
            if (d[t.first][state] > d[t.first][t.second]) {
                d[t.first][state] = d[t.first][t.second];
                q.push_front({ t.first,state });
            }
        }
        // 遍历当前点能到的所有点
        for (int i = 0; i < vec[t.first].size(); ++i) {
            int j = vec[t.first][i].first; // 邻点的编号
            int m = vec[t.first][i].second;// 与邻点之间是否有不可通过的门
            // 如果有且没有打开这扇门的钥匙,就 continue
            if (m && !(t.second >> m & 1))continue;
            // 更新当前点的最短距离
            if (d[j][t.second] > d[t.first][t.second] + 1) {
                d[j][t.second] = d[t.first][t.second] + 1;
                q.push_back({ j,t.second });
            }
        }
    }
    return -1;
}
void solve() {
    cin >> n >> m >> p;
    int t = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            mp[i][j] = ++t;
        }
    }
    int k; cin >> k;
    while (k--) {
        int x1, y1, x2, y2, g; cin >> x1 >> y1 >> x2 >> y2 >> g;
        int a = mp[x1][y1], b = mp[x2][y2];
        edges.insert({ a,b });
        edges.insert({ b,a });
        if (g) {
            vec[a].push_back({ b,g });
            vec[b].push_back({ a,g });
        }
    }
    build();
    int s; cin >> s;
    while (s--) {
        int x, y, q; cin >> x >> y >> q;
        key[mp[x][y]] |= 1 << q;
    }
    cout << bfs() << endl;
}
signed main() {
    //int _; cin >> _;
    //while (_--)
    solve();
    return 0;
}





目录
相关文章
|
1月前
acwing 1010 拦截导弹
acwing 1010 拦截导弹
30 1
|
1月前
acwing 188 武士风度的牛
acwing 188 武士风度的牛
8 0
|
1月前
acwing 1107 魔板
acwing 1107 魔板
10 0
|
1月前
acwing 1116 马走日
acwing 1116 马走日
10 0
|
1月前
acwing 285. 没有上司的舞会
acwing 285. 没有上司的舞会
15 0
|
1月前
acwing 1014 登山
acwing 1014 登山
27 0
|
1月前
acwing 1012 友好城市
acwing 1012 友好城市
15 0
|
1月前
acwing 1017 怪盗基德的滑翔翼
acwing 1017 怪盗基德的滑翔翼
27 0
|
1月前
acwing 275 传纸条 (线性dp)
acwing 275 传纸条 (线性dp)
13 0
|
6月前
|
人工智能
acwing 5478. 分班
acwing 5478. 分班