1. 程式人生 > >洛谷4011 孤島營救問題(BFS)(狀態壓縮)

洛谷4011 孤島營救問題(BFS)(狀態壓縮)

題目

1944 年,特種兵麥克接到國防部的命令,要求立即趕赴太平洋上的一個孤島,營救被敵軍俘虜的大兵瑞恩。瑞恩被關押在一個迷宮裡,迷宮地形複雜,但幸好麥克得到了迷宮的地形圖。迷宮的外形是一個長方形,其南北方向被劃分為 N 行,東西方向被劃分為 M 列,於是整個迷宮被劃分為 N×M 個單元。每一個單元的位置可用一個有序數對(單元的行號,單元的列號)來表示。南北或東西方向相鄰的 2 個單元之間可能互通,也可能有一扇鎖著的門,或者是一堵不可逾越的牆。迷宮中有一些單元存放著鑰匙,並且所有的門被分成P 類,開啟同一類的門的鑰匙相同,不同類門的鑰匙不同。

大兵瑞恩被關押在迷宮的東南角,即(N,M) 單元裡,並已經昏迷。迷宮只有一個入口,在西北角。也就是說,麥克可以直接進入 (1,1)單元。另外,麥克從一個單元移動到另一個相鄰單元的時間為 1,拿取所在單元的鑰匙的時間以及用鑰匙開門的時間可忽略不計。

試設計一個演算法,幫助麥克以最快的方式到達瑞恩所在單元,營救大兵瑞恩。

題解

BFS+狀態壓縮
S才14果斷狀壓拿了多少把鑰匙。
設vis[x][y][t]表示到(x,y)且鑰匙狀態為t時的最短時間,很顯然每個位置每個狀態只能走一次。

程式碼

#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=1<<30;
const int maxn=12;
const int dx[]={0,0,-1,1},dy[]={-1,1,0,0};
int n,m,p;
vector<int> key[maxn][maxn];
int ma[maxn][maxn][maxn][maxn];
int vis[maxn][maxn][(1<<14)+10];

struct U{int x,y,t;};
queue<U> q;

void bfs()
{
    int t0=0;
    for(int i=0;i<key[1][1].size();i++) t0|=1<<key[1][1][i]-1;
    q.push((U){1,1,t0});vis[1][1][t0]=1;
    while(!q.empty())
    {
        int x=q.front().x,y=q.front().y,t=q.front().t;q.pop();
        for(int i=0;i<4;i++)
        {
            int nx=x+dx[i],ny=y+dy[i],nt=t;
            if(1>nx||nx>n || 1>ny||ny>m) continue;
            if(ma[x][y][nx][ny]==-1) continue;
            if(!ma[x][y][nx][ny] || (t>>ma[x][y][nx][ny]-1)&1)//p>>ma[x][y][nx][ny]-1
            {
                for(int j=0;j<key[nx][ny].size();j++) nt|=1<<key[nx][ny][j]-1;
                if(vis[nx][ny][nt]) continue;vis[nx][ny][nt]=vis[x][y][t]+1;
                q.push((U){nx,ny,nt});
//                printf("q: %d %d %d - %d\n",nx,ny,nt,vis[nx][ny][nt]);
            }
        }
    }
}

int main()
{
    scanf("%d%d%d",&n,&m,&p);
    int K;scanf("%d",&K);
    for(int i=1;i<=K;i++)
    {
        int x1,y1,x2,y2,g;
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&g);
        if(!g) ma[x1][y1][x2][y2]=ma[x2][y2][x1][y1]=-1;
        else ma[x1][y1][x2][y2]=ma[x2][y2][x1][y1]=g;
    }
    int S;scanf("%d",&S);
    for(int i=1;i<=S;i++)
    {
        int x,y,q;
        scanf("%d%d%d",&x,&y,&q);
        key[x][y].push_back(q);
    }
    bfs();
    int ans=inf;
    for(int i=0,imax=1<<p;i<imax;i++) ans=min(ans,!vis[n][m][i]?inf:vis[n][m][i]);//debug imax=1<<p-1 max(ans,vis[n][m][i])
    if(ans!=inf) printf("%d\n",ans-1);
    else puts("-1");
    return 0;
}