【题解】孤岛营救问题之拯救呆瓜大头兵 (普通bfs+状压)

孤岛营救问题

题目传送门:洛谷P4011 孤岛营救问题

题目描述

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

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

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

输入格式

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

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

\(I+2\)\((1\leq I\leq 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 P\))。

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

输出格式

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

样例 #1

样例输入 #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

样例输出 #1

14

提示

\(|X_{i1}-X_{i2}|+|Y_{i1}-Y_{i2}|=1,0\leq G_i\leq P\)

\(1\leq Q_i\leq P\)

\(N,M,P\leq10, K<150,S\leq 14\)

思路1:普通bfs+状压

普通bfs问题 布吉岛和网络流有什么勾巴关系

每走到一个位置先判断是否合法 有没有墙
合法且无墙的话 判断有没有门
要是有门没有钥匙的话就跳过
否则 就走下去 有钥匙就拿上钥匙,没钥匙就继承上一个位置的钥匙
反正拿钥匙不浪费时间能拿就拿

\(key_i\)=0010表示走到编号为i的格子时候有第1类门的钥匙,当遇到门的时候直接\(key_i\)>>x&1判断第x类门有没有钥匙
d[x][y][\(key_i\)]表示在(x,y)时,所拥有的钥匙状态为\(key_i\) 储存最少步数
用式子把(x,y)变成编号num ,d[x][y][\(key_i\)]\(\implies\)d[num][\(key_i\)],优化出一维的空间出来

代码实现:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int N=400,M=13;
#define PII pair<int,int>
queue<PII>q;
int n,m,p,k;
int ax[]={0,0,0,1,-1},ay[]={0,1,-1,0,0};
int g[400][400],st[N][1<<M],d[N][1<<M],key[1<<M];
inline int get(int x,int y)
{
    return (x-1)*m+y;
}
int bfs()
{
    memset(d,0x3f3f3f3f,sizeof d);
    int start=get(1,1);
    q.push({start,key[start]});
    d[start][key[start]]=0;
    st[start][key[start]]=true;
    while(q.size())
    {
        auto t=q.front();q.pop();
        
        int num=t.first,v=t.second;
        
        for(int i=1;i<=4;i++)
        {
            int x1=(num-1)/m+ax[i]+1,y1=(num-1)%m+ay[i]+1;
            int newnum=get(x1,y1),newv=v;
            if(!x1||!y1||x1>n||y1>m||!g[num][newnum]) continue;
            if(g[num][newnum]!=-1&&!(v>>g[num][newnum]&1))
            {
                continue;
            }
            newv|=key[newnum];
            if(!st[newnum][newv])
            {
                d[newnum][newv]=d[num][v]+1;
                st[newnum][newv]=true;
                q.push({newnum,newv});
            }
            if(newnum==n*m) return d[newnum][newv];
        }
        
        
    }
    return -1;
    
}
int main()
{
    memset(g,-1,sizeof g);
    cin>>n>>m>>p>>k;
    for(int i=1;i<=k;i++)
    {
        int x1,y1,x2,y2,t;
        cin>>x1>>y1>>x2>>y2>>t;
        int z1=get(x1,y1),z2=get(x2,y2);
        g[z1][z2]=g[z2][z1]=t;
        
    }
    int s;
    cin>>s;
    
    for(int i=1;i<=s;i++)
    {
        int x,y,q;
        cin>>x>>y>>q;
        key[get(x,y)]|=(1<<q);
    }
    cout<<bfs();
    return 0;
}

posted @ 2022-07-11 10:04  watasky  阅读(66)  评论(0)    收藏  举报