Processing math: 100%

Luogu P4011 孤岛营救问题(状态压缩+最短路)

P4011 孤岛营救问题

题意

题目描述

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

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

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

输入输出格式

输入格式:

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

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

I+2(1IK),有5个整数,依次为Xi1,Yi1,Xi2,Yi2,Gi

  • Gi1时,表示(Xi1,Yi1)单元与(Xi2,Yi2)单元之间有一扇第Gi类的门
  • Gi=0时,表示(Xi1,Yi1)单元与(Xi2,Yi2)单元之间有一堵不可逾越的墙(其中,|Xi1Xi2|+|Yi1Yi2|=10GiP)。

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

K+3+J(1JS),有3个整数,依次为Xi1,Yi1,Qi:表示第J把钥匙存放在(Xi1,Yi1)单元里,并且第J把钥匙是用来开启第Qi类门的。(其中1QiP)。

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

输出格式:

将麦克营救到大兵瑞恩的最短时间的值输出。如果问题无解,则输出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

说明

|Xi1Xi2|+|Yi1Yi2|=1,0GiP

1QiP

N,M,P10,K<150,S14

思路

这题我调了一晚上,本来想第二天给你们装逼的,可惜没调出来。 --digger_sun

这是一道很不错的题,可以用多种方式ACA、最短路、网络流...在这里我选择的是状态压缩+最短路。

定义dis[i][j]表示在i号结点时拥有的钥匙状态为j的最短路径长度。其中,j的二进制表示下第k位表示是否拥有钥匙k。建图时,互相能连通的两点之间距离都为一,走到有钥匙的点时一定拿钥匙,稳妥地处理输入信息,就能AC了,码量也不大。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int INF=0x3f3f3f3f;
int n,m,p,k,ans=INF,dis[105][1<<15],G[105][105],gate[105][105],key[105];
int cnt,top[105],to[1005],len[1005],ned[1005],nex[1005];
bool vis[105][1<<15];
int a[2]={+1,+0};
int b[2]={+0,+1};
int read()
{
    int re=0;
    char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
    return re;
}
inline int f(int x,int y){return (x-1)*m+y;}
inline void add_edge(int x,int y,int z,int w)
{
    to[++cnt]=y,len[cnt]=z,ned[cnt]=w,nex[cnt]=top[x],top[x]=cnt;
    to[++cnt]=x,len[cnt]=z,ned[cnt]=w,nex[cnt]=top[y],top[y]=cnt;
}
void SPFA()
{
    memset(dis,0x3f,sizeof dis);
    queue<PII>Q;
    dis[1][key[1]]=0;
    Q.push(make_pair(1,key[1]));
    while(!Q.empty())
    {
        int now=Q.front().first,con=Q.front().second;Q.pop();
        vis[now][con]=false;
        for(int i=top[now];i;i=nex[i])
            if((ned[i]&con)==ned[i])
                if(dis[to[i]][con|key[to[i]]]>dis[now][con]+len[i])
                {
                    dis[to[i]][con|key[to[i]]]=dis[now][con]+len[i];
                    if(!vis[to[i]][con|key[to[i]]])
                    {
                        vis[to[i]][con|key[to[i]]]=true;
                        Q.push(make_pair(to[i],con|key[to[i]]));
                    }
                }
    }
}
int main()
{
    n=read(),m=read(),p=read(),k=read();
    while(k--)
    {
        int x=read(),y=read(),xx=read(),yy=read(),g=read();
        if(!g) G[f(x,y)][f(xx,yy)]=G[f(xx,yy)][f(x,y)]=INF;
        else gate[f(x,y)][f(xx,yy)]=gate[f(xx,yy)][f(x,y)]=(1<<(g-1));
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            for(int w=0;w<2;w++)
            {
                int x=i+a[w],y=j+b[w];
                if(x<=0||x>n||y<=0||y>m) continue;
                add_edge(f(i,j),f(x,y),G[f(i,j)][f(x,y)]==INF?INF:1,gate[f(i,j)][f(x,y)]);
            }
    k=read();
    while(k--)
    {
        int x=read(),y=read(),z=read();
        key[f(x,y)]|=(1<<(z-1));
    }
    SPFA();
    for(int i=0;i<(1<<p);i++) ans=min(ans,dis[f(n,m)][i]);
    printf("%d",ans==INF?-1:ans);
    return 0;
}
posted @   UranusITS  阅读(147)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示