[bsoj1823] 拯救Angel行动


题目描述

  一切危险都结束了。JLHS人在Angel的带领下来到了新的新的大陆,并且来到了一个奇怪的地方(今江苏南京)。这里的土著人对他们似乎不是很友好(中国以前也有土著?!),在短暂的交涉以后,他们把带头大哥Angel抓了起来,并且放到了一个迷宫当中。
  土著人比JLHS岛上的奇怪生物明智多了,他们把Angel关在了一个N*M(M, N<=15)的迷宫中(制作成本高了,迷宫就小了……),迷宫里有不可逾越的墙和P种门(p<=10)。从一个格子移动到另一个的时间是1,拿所在格子的钥匙的时间以及用钥匙开门的时间不计。你的任务是用最少的时间救出Angel(粗心的mld忘记说了:救援队一开始在1,1点,Angel在n,m点) 。


输入格式

本题有多组数据,第1行3个整数,表示N,M,P的值。第2行1个整数K,表示迷宫中门的墙的总个数。以后K行,每行5个数,分别为X1,Y1,X2,Y2和G。 G>0时,代表(X1,Y1)->(X2,Y2)有一个G类的门。 G=0时,代表(X1,Y1)->(X2,Y2)有一个墙,其中|X1-X2|+|Y1-Y2|=1。然后是一个整数S,代表了钥匙的个数然后S行,每行3个数,X,Y,Q,代表(X,Y)位置有一个开启Q门的钥匙。


输出格式

对于每组数据,一行输出包含一个整数,代表救出Angel的最少时间,若不能救出,则输出“Poor Angel!”


样例数据

样例输入

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


题目分析

[网络流24题-14] 孤岛营救一样
使用分层图,注意多组数据


源代码

#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() {
    while(scanf("%d%d%d",&Row,&Line,&DoorKind)!=EOF) {
        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("Poor Angel!");
        else printf("%d\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值