最大流——BZOJ1189/Luogu3191 [HNOI2007]紧急疏散evacuate

题面:BZOJ1189 Luogu3191
首先我们预处理出每个空地到每扇门的最短时间
然后我们二分时间t,然后把每扇门拆成t个点
接下来建图:超级原点->每块空地(流1),每扇门的每一个时刻->超级汇点(流1)
然后对于每块空地,枚举能够在t时间内到达的门,连上边(流1,空地->门(对应的最短时间那个时刻点))
然后等待的问题只要每扇门的某一时刻点向下一时刻点连上就行了(流INF)
然后跑最大流判断是否满流(流为所有人)就好了

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ctime>
#include <map>
#include <queue>
#include <cstdlib>
#include <string>
#include <climits>
#include <set>
#include <vector>
using namespace std;
struct ppap{int x,y;};
const int dx[5]={0,1,-1,0,0};
const int dy[5]={0,0,0,1,-1};
bool vis[401][401];
int d[201][201][201],n,m,s,t,mn=0,rp[401][401];
char S[401];
int nedge=-1,p[500001],c[500001],nex[500001],head[500001];
int dist[500001],cnt=0,cur[500001];
inline void addedge(int x,int y,int z){
    p[++nedge]=y;c[nedge]=z;nex[nedge]=head[x];head[x]=nedge;
}
inline bool bfs(int s,int t){
    queue<int>q;q.push(s);
    memset(dist,-1,sizeof dist);dist[s]=1;
    while(!q.empty()){
        int now=q.front();q.pop();
        for(int k=head[now];k>-1;k=nex[k])if(c[k]&&dist[p[k]]==-1){
            dist[p[k]]=dist[now]+1;q.push(p[k]);
        }
    }
    return dist[t]>-1;
}
inline int dfs(int x,int low){
    if(x==t)return low;
    int a,used=0;
    for(int k=cur[x];k>-1;k=nex[k])if(c[k]&&dist[p[k]]==dist[x]+1){
        a=dfs(p[k],min(c[k],low-used));
        if(a)c[k]-=a,c[k^1]+=a,used+=a;
        if(c[k])cur[x]=k;
        if(used==low)break;
    }
    if(!used)dist[x]=-1;
    return used;
}
inline int dinic(){
    int flow=0;
    while(bfs(s,t)){
        for(int i=s;i<=t;i++)cur[i]=head[i];
        flow+=dfs(s,1e9);
    }
    return flow;
}
inline void spfa(int co,int x,int y){
    queue<ppap>q;q.push((ppap){x,y});
    memset(vis,0,sizeof vis);vis[x][y]=1;d[co][x][y]=0;
    while(!q.empty()){
        ppap now=q.front();q.pop();
        for(int i=1;i<5;i++){
            int xx=now.x+dx[i],yy=now.y+dy[i];
            if(xx<1||yy<1||xx>n||yy>m||rp[xx][yy]!=0)continue;
            if(d[co][xx][yy]>d[co][now.x][now.y]+1){
                d[co][xx][yy]=d[co][now.x][now.y]+1;
                if(!vis[xx][yy])vis[xx][yy]=1,q.push((ppap){xx,yy});
            }
        }vis[now.x][now.y]=0;
    }
}
inline bool check(int x){
    s=0;t=n*m+mn*x+1;
    nedge=-1;memset(nex,-1,sizeof nex);memset(head,-1,sizeof head);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)if(rp[i][j]==0)addedge(s,(i-1)*m+j,1),addedge((i-1)*m+j,s,0);
    for(int i=1;i<=mn;i++)
        for(int j=1;j<=x;j++)addedge(n*m+(i-1)*x+j,t,1),addedge(t,n*m+(i-1)*x+j,0);
    for(int i=1;i<=mn;i++)
        for(int j=1;j<x;j++)addedge(n*m+(i-1)*x+j,n*m+(i-1)*x+j+1,1e9),addedge(n*m+(i-1)*x+j+1,n*m+(i-1)*x+j,0);
    for(int k=1;k<=mn;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)if(rp[i][j]==0&&d[k][i][j]<=x)addedge((i-1)*m+j,n*m+(k-1)*x+d[k][i][j],1),addedge(n*m+(k-1)*x+d[k][i][j],(i-1)*m+j,0);
    return dinic()==cnt;
}
int main()
{
    memset(rp,-1,sizeof rp);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%s",S+1);
        for(int j=1;j<=m;j++)if(S[j]=='.')rp[i][j]=0,cnt++;
        else if(S[j]=='D')rp[i][j]=++mn;
    }
    for(int i=1;i<=mn;i++)for(int j=1;j<=n;j++)for(int k=1;k<=m;k++)d[i][j][k]=1e9;
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(rp[i][j]>0)spfa(rp[i][j],i,j);
    int l=0,r=1000,ans=1e9;
    while(l<=r){
        int mid=l+r>>1;
        if(check(mid))ans=min(ans,mid),r=mid-1;
        else l=mid+1;
    }
    if(ans!=1e9)printf("%d",ans);
    else puts("impossible");
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值