网络流24题(十四)

网络流24题(十四)

十四、孤岛营救问题

题目描述

1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图。迷宫的外形是一个长方形,其南北方向被划分为 N 行,东西方向被划分为 M 列,于是整个迷宫被划分为 N×M 个单元。每一个单元的位置可用一个有序数对(单元的行号,单元的列号)来表示。南北或东西方向相邻的 2 个单元之间可能互通,也可能有一扇锁着的门,或者是一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分成P 类,打开同一类的门的钥匙相同,不同类门的钥匙不同。

大兵瑞恩被关押在迷宫的东南角,即 (N,M) 单元里,并已经昏迷。迷宫只有一个入口,在西北角。也就是说,麦克可以直接进入 (1,1) 单元。另外,麦克从一个单元移动到另一个相邻单元的时间为 1,拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。

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

输入格式

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

第 2 行是 1 个整数 K,表示迷宫中门和墙的总数。

\(I+2\)\((1≤I≤K)\),有 5 个整数,依次为\(X_{i1},Y_{i1},X_{i2},Y_{i2},G_i\)

  • \(G_i \geq 1\) 时,表示 \((X_{i1},Y_{i1})\) 单元与 \((X_{i2},Y_{i2})\) 单元之间有一扇第 \(G_i\) 类的门

  • \(G_i=0\) 时,表示 \((X_{i1},Y_{i1})\) 单元与 \((X_{i2},Y_{i2})\) 单元之间有一堵不可逾越的墙(其中,\(|X_{i1}-X_{i2}|+|Y_{i1}-Y_{i2}|=1,0\leq G_i\leq P\))。

\(K+3\) 行是一个整数 \(S\),表示迷宫中存放的钥匙总数。

\(K+3+J\)\((1\leq J\leq S)\),有 3 个整数,依次为 \(X_{i1},Y_{i1},Q_i\)​:表示第 \(J\) 把钥匙存放在 \((X_{i1},Y_{i1})\)单元里,并且第 \(J\) 把钥匙是用来开启第 \(Q_i\) 类门的。(其中\(1\leq Q_i\leq\))。

输入数据中同一行各相邻整数之间用一个空格分隔。

输出格式

将麦克营救到大兵瑞恩的最短时间的值输出。如果问题无解,则输出 -1。

题解

模型:

状压分层图最短路
有没有某把钥匙是两种状态,考虑二进制状压表示钥匙的有无,根据状态的变化还要建立分层图,分层图之间的边长为0。
因为边权为1或者0所以最后求最短路使用bfs即可。

建图与实现:

将坐标\((i,j)\)压缩为一个正整数\(x\)
\(G[x_1][x_2] = q\)表示从\(x_1\)\(x_2\)需要第\(q\)把钥匙通行,-1表示任意通行,0不通行。
\(f[i][x]\)表示到第\(i\)\(x\)坐标时的最短路。
\(key[x]\)表示\(x\)位置的钥匙。(这个傻逼题目一个位置有多把钥匙,一个钥匙可以无穷用)
然后\(bfs\)跑就行。

代码

ll n,m,p,num;
const ll N = (1<<14)+5;
ll G[12*12][12*12];//需要钥匙
bool vis[N][12*12] = {false};
ll f[N][12*12];
vector<ll> key[12*12];
#define  pr pair<ll,ll>//层数,位置
ll to[5][5] = {{0,1},{0,-1},{-1,0},{1,0}};
void bfs(){
    memset(f,0x3f,sizeof f);
    queue<pr> q;
    q.push({0,1});
    vis[0][1] = true;
    f[0][1] = 0;
    while(!q.empty()){
        pr u = q.front();
        q.pop();
        //cout<<u.first<<' '<<u.second<<' '<<key[u.second]<<endl;
        for(auto k:key[u.second]) {
            if (!(u.first >> (k - 1) & 1)) {
                ll t = u.first + (1 << (k - 1));
                //cout<<t<<endl;
                if (!vis[t][u.second]) {
                    q.push({t, u.second});
                    f[t][u.second] = f[u.first][u.second];
                    vis[t][u.second] = true;
                }
            }
        }
        ll x1 = (u.second-1)/m+1,y1 = (u.second-1)%m+1;
        for(ll i = 0;i < 4;i++){
            ll x2 = x1+to[i][0],y2 = y1+to[i][1];
            if(x2<=n&&x2>=1&&y2<=m&&y2>=1){
                ll jg = G[(x1-1)*m+y1][(x2-1)*m+y2];
                if(jg == 0)continue;
                if(((u.first>>(jg-1))&1) || jg == -1) {
                    if(!vis[u.first][(x2 - 1) * m + y2]) {
                        f[u.first][(x2 - 1) * m + y2] = f[u.first][u.second]+1;
                        q.push({u.first, (x2 - 1) * m + y2});
                        vis[u.first][(x2 - 1) * m + y2] = true;
                    }
                }
            }
        }
    }
    ll ans = 0x3f3f3f3f;
    for(ll i = 0;i <= (1<<p)-1;i++){
        ans = min(ans,f[i][n*m]);
    }
    if(ans == 0x3f3f3f3f)ans = -1;
    cout<<ans<<endl;
}
int main() {
    cin>>n>>m>>p;
    ll k;cin>>k;
    memset(G,-1,sizeof G);
    for(ll i = 1;i <= k;i++){
        ll x1,y1,x2,y2,g;cin>>x1>>y1>>x2>>y2>>g;
        ll u = (x1-1)*m+y1,v = (x2-1)*m+y2;
        //cout<<u<<' '<<v<<endl;
        G[u][v] = G[v][u] = g;
    }
    cin>>num;
    memset(key,0,sizeof key);
    for(ll i = 1;i <= num;i++){
        ll x,y,o;cin>>x>>y>>o;
        key[(x-1)*m+y].push_back(o);
    }
    bfs();
    return 0;
}
posted @ 2021-10-20 16:19  Paranoid5  阅读(24)  评论(0编辑  收藏  举报