[网络流24题-14] 孤岛营救 - 分层图


题目描述

1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图。迷宫的外形是一个长方形,其南北方向被划分为N 行,东西方向被划分为M列,于是整个迷宫被划分为 N×M 个单元。每一个单元的位置可用一个有序数对(单元的行号,单元的列号)来表示。南北或东西方向相邻的 2 个单元之间可能互通,也可能有一扇锁着的门,或者是一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分成 P类,打开同一类的门的钥匙相同,不同类门的钥匙不同。
  大兵瑞恩被关押在迷宫的东南角,即(N,M)单元里,并已经昏迷。迷宫只有一个入口,在西北角。也就是说,麦克可以直接进入(1,1)单元。另外,麦克从一个单元移动到另一个相邻单元的时间为1,拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。
  这里写图片描述
试设计一个算法,帮助麦克以最快的方式到达瑞恩所在单元,营救大兵瑞恩。


输入格式

  第一行是三个整数,依次表示N,M,P的值;(3≤N,M≤15,1≤P≤10)
  第二行是一个整数K,表示迷宫中门和墙的总个数;
  第i+2行(1≤i≤K),有5个整数,依次为Xi1,Yi1,Xi2,Yi2,Gi:
  当Gi≥1 时,表示(Xi1,Yi1)单元与(Xi2,Yi2) 单元之间有一扇第Gi类的门,当Gi=0时, 表示(Xi1,Yi1)单元与(Xi2,Yi2) 单元之间有一堵不可逾越的墙;
(其中,|Xi1-Xi2|+|Yi1-Yi2|=1,0≤Gi≤P)
  第K+3 行是一个整数S,表示迷宫中存放的钥匙总数;
  第K+3+j行(1≤j≤S),有3个整数,依次为Xi1,Yi1,Qi:表示第j把钥匙存放在(Xi1,Yi1) 单元里,并且第j把钥匙是用来开启第Qi类门的。(其中1≤Qi≤P)
  注意:输入数据中同一行各相邻整数之间用一个空格分隔。


输出格式

输出一个整数T,表示麦克营救到大兵瑞恩的最短时间的值,若不存在可行的营救方案则输出-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


题目分析

分层图,调试爆炸。。。
样例数据太大了这里给一份自己造的样例:
3 3 2
5
1 2 2 2 0
1 3 2 3 0
1 1 2 1 1
2 1 2 2 2
3 1 3 2 0
2
1 3 1
3 1 2
图如下:
这里写图片描述
枚举拥有钥匙的状态,并以此分层,故有2^p层。
如上图,将图分为
第0层:没有钥匙
第1层:拥有钥匙1
第2层:拥有钥匙2
第3层:拥有钥匙1,2
若每一层有未拥有的钥匙,就向它所代表的层的钥匙处建一条边,边权为0
每层图内建立边,边权为1,其中没有钥匙的门和墙不能走。
然后调上1个小时就出来了233


源代码

#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
inline const int Get_Int() {
    int num=0,bj=1;
    char x=getchar();
    while(x<'0'||x>'9') {
        if(x=='-')bj=-1;
        x=getchar();
    }
    while(x>='0'&&x<='9') {
        num=num*10+x-'0';
        x=getchar();
    }
    return num*bj;
}
const int maxn=500005;
struct Edge {
    int from,to,dist;
};
struct Spfa {
    int n,m;
    vector<Edge>edges;
    vector<int>G[maxn];
    bool inque[maxn];
    int dist[maxn],used[maxn],path[maxn];
    void init(int n) {
        this->n=n;
        edges.clear();
        for(int i=0; i<=n; i++)G[i].clear();
    }
    void AddEdge(int from,int to,int dist) {
        edges.push_back((Edge) {
            from,to,dist
        });
        m=edges.size();
        G[from].push_back(m-1);
    }
    bool main(int s) {
        for(int i=0; i<=n; i++)dist[i]=0x7fffffff/2;
        memset(inque,0,sizeof(inque));
        memset(used,0,sizeof(used));
        deque<int>Q;
        Q.push_back(s);
        dist[s]=0;
        path[s]=s;
        inque[s]=1;
        used[s]++;
        while(!Q.empty()) {
            int Now=Q.front();
            Q.pop_front();
            inque[Now]=0;
            for(int i=0; i<G[Now].size(); i++) {
                Edge& e=edges[G[Now][i]];
                int Next=e.to;
                if(dist[Next]>dist[Now]+e.dist) {
                    dist[Next]=dist[Now]+e.dist;
                    path[Next]=Now;
                    if(!inque[Next]) {
                        used[Next]++;
                        if(used[Next]==edges.size())return false; //负权回环
                        if(!Q.empty()&&dist[Next]<dist[Q.front()])Q.push_front(Next); //SLF优化
                        else Q.push_back(Next);
                        inque[Next]=1;
                    }
                }
            }
        }
        return true;
    }
};
const int Dirx[]= {0,1,0},Diry[]= {0,0,1};
int n,Floor,Floorn,Row,Line,DoorKind,Door,Keynum,KeynumofKind[2005],map[505][505],ans=0x7fffffff/2;
struct Point {
    int x,y;
} Key[2005][2005];
int GetPos(int x,int y) { //二维坐标 
    return (x-1)*Line+y;
}
int GetPos(int FloorNum,int x,int y) { //三维坐标 
    return Floor*FloorNum+GetPos(x,y);
}
Spfa spfa;
void Build_Graph() {
    bool HaveKey[2005];
    Floor=Row*Line;
    Floorn=(1<<DoorKind);
    n=Floor*Floorn;
    spfa.init(n);
    for(int Floornum=0; Floornum<Floorn; Floornum++) { //层(同时压缩有2进制所拥有钥匙)
        for(int i=1; i<=Row; i++)
            for(int j=1; j<=Line; j++) { //建立同层图 
                int Now=GetPos(i,j);
                for(int k=1; k<=2; k++) {
                    int Nextx=i+Dirx[k],Nexty=j+Diry[k],Next=GetPos(Nextx,Nexty);
                    if(Nextx<1||Nextx>Row||Nexty<1||Nexty>Line||map[Now][Next]==-1)continue;
                    if(map[Now][Next]==0||(Floornum&1<<(map[Now][Next]-1))) {
                        spfa.AddEdge(GetPos(Floornum,i,j),GetPos(Floornum,Nextx,Nexty),1);
                        spfa.AddEdge(GetPos(Floornum,Nextx,Nexty),GetPos(Floornum,i,j),1);
                    }
                }
            }
        for(int i=1; i<=Keynum; i++) { //建立跨维边 
            if((Floornum&1<<(i-1))==0) {
                int Status=Floornum^(1<<(i-1));
                for(int j=1; j<=KeynumofKind[i]; j++) {
                    int Now=GetPos(Floornum,Key[i][j].x,Key[i][j].y),Next=GetPos(Status,Key[i][j].x,Key[i][j].y);
                    spfa.AddEdge(Now,Next,0);
                }
            }
        }
    }
}
int main() {
    Row=Get_Int();
    Line=Get_Int();
    DoorKind=Get_Int();
    Door=Get_Int();
    for(int i=1; i<=Door; i++) {
        int x1=Get_Int(),y1=Get_Int(),x2=Get_Int(),y2=Get_Int(),Kind=Get_Int();
        if(Kind==0)Kind=-1;
        map[GetPos(x1,y1)][GetPos(x2,y2)]=map[GetPos(x2,y2)][GetPos(x1,y1)]=Kind;
    }
    Keynum=Get_Int();
    for(int i=1; i<=Keynum; i++) {
        int x=Get_Int(),y=Get_Int();
        int Kind=Get_Int();
        KeynumofKind[Kind]++;
        Key[Kind][KeynumofKind[Kind]].x=x;
        Key[Kind][KeynumofKind[Kind]].y=y;
    }
    Build_Graph();
    int Start=GetPos(0,1,1);
    spfa.main(Start);
    for(int i=0; i<Floorn; i++)ans=min(ans,spfa.dist[GetPos(i,Row,Line)]);
    if(ans==0x7fffffff/2)puts("-1");
    else printf("%d\n",ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值