Prison Break

题目描述
Scofild又要策划一次越狱行动,和上次一样,他已经掌握了整个监狱的地图,看守的位置,以及准备好了逃出监狱的出口。由于消息被其他监狱中的囚犯得知了,为了不泄露消息,他不得不将所有人带出监狱。
这是一个月黑风高的夜晚。。。看守们都已经睡着,在没有罪犯打扰的情况下绝对不会醒来,即罪犯不能到达看守所在位置。在每一个空地中,都有一名罪犯,并且同一个空地,能容纳无穷多个罪犯。每个人都只能向东南西北四个方向移动。在地图中某些位置,有一些出口,当罪犯到达出口,就视为逃出监狱,并且出口每一秒钟最多能逃出1个罪犯。现在越狱行动开始。。。Scofild需要安排一个越狱计划,使得大家能尽快的逃出监狱。此时,监狱的监控发现了情况,监狱外的警察,将在T秒后到达现场,并封锁所有出口。现在Scofild想要知道所有人能否成功越狱,如果能,计算出所需的最短的越狱时间,使得最后一个人逃出监狱的时间尽量的短。

输入
第一行三个整数r,c,T(3 <= r, c <= 12)
接下来r行字符,每行c列。‘.’表示一个空地,一开始该点有一名罪犯。‘X’表示警察的位置,并且他不能移动,罪犯也不能到达该点,否则他就会醒来并拉响警报。‘E’表示出口。

输出
1行,输出最少的越狱时间,如果在大批警察赶到之后还有人无法逃离,则输出“impossible”。

样例输入
5 5 3
XXEXX
X…X
E…X
X…E
XXXXX
样例输出
3

每秒钟每个出口只能逃出一个罪犯,这种一一对应的匹配关系,让我们自然而然地想到了二分图最大匹配。我们可以对每个出口与时间,进行裂点操作。先bfs预处理出每个犯罪者到某出口的最短时间,设mintime=x,
则把罪犯与该点的x时间–总时间都连一条边。
那么问题又来了,题目要求我们找出最短的越狱时间,但是普通的最大匹配只是保证匹配完成,并不会管时间怎么样。所以我们可以枚举最短时间,然后匹配的话,如果时间超出当前限制,则不与它匹配,这个枚举时间的过程还可以用二分进一步优化。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
int head[200],Next[3000000],to[3000000],p[15][15],e[15][15];
int xy[20000],mark[20000];
char a[15][15];
int x[200],y[200],z[200],dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int n,m,Time,tot,es,crime,now,all,limit,l,r;
void add(int x,int y)
{
    tot++;
    Next[tot]=head[x];
    to[tot]=y;
    head[x]=tot;
}
void bfs(int X,int Y)
{
    crime++;
    head[crime]=-1;
    int t=0,w=1,len=0;
    memset(p,0,sizeof(p));
    x[1]=X;y[1]=Y;
    p[X][Y]=1;
    while(t<w) 
    {
        t++;
        if(a[x[t]][y[t]]=='E') 
        {
            for(int i=Time;i>=z[t];i--) add(crime,(e[x[t]][y[t]]-1)*Time+i);
            continue;
        }
        for(int i=0;i<4;i++) 
        {
            int rx=x[t]+dx[i];
            int ry=y[t]+dy[i];
            if(rx>=1&&rx<=n&&ry>=1&&ry<=m&&p[rx][ry]==0&&a[rx][ry]!='X') 
            {
                w++;
                x[w]=rx;
                y[w]=ry;
                z[w]=z[t]+1;
                p[rx][ry]=1;
            }
        }
    }
}
int dfs(int k)
{
    for(int i=head[k];i!=-1;i=Next[i]) 
    if(mark[to[i]]!=now) 
    {
        mark[to[i]]=now;
        int cost=to[i]%Time;
        if(cost==0) cost=Time;
        if(cost>limit) continue;
        if(xy[to[i]]==-1||dfs(xy[to[i]])) 
        {
            xy[to[i]]=k;
            return 1;
        }
    }
    return 0;
}
int match()
{
    all=es*Time;
    int res=0;
    l=1;r=Time;
    while(l<=r)
    {
        limit=(l+r)/2;
        for(int j=1;j<=all;j++) 
        {
            xy[j]=-1;
            mark[j]=0;
        }
        now=0,res=0;
        for(int j=1;j<=crime;j++) 
        {
            now++;
            if(dfs(j)==0) break; else res++;
        }
        if(res==crime) r=limit-1;else l=limit+1;
    }
    return l;
}
int main()
{
    cin>>n>>m>>Time;
    for(int i=1;i<=n;i++) scanf("%s",a[i]+1);
    for(int i=1;i<=n;i++) 
    for(int j=1;j<=m;j++) 
    if(a[i][j]=='E')
    {
        es++;
        e[i][j]=es;
    }
    for(int i=1;i<=n;i++) 
    for(int j=1;j<=m;j++) 
    if(a[i][j]=='.') bfs(i,j);
    if(match()>Time) 
    {
        cout<<"impossible"<<endl;
        return 0;
    }
    cout<<l;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值