BZOJ1189: [HNOI2007]紧急疏散evacuate(二分+最大流拆点法)

题意:传送门

题解:直接二分答案 mid,也就是所有人安全撤离所需时间, 通过最大流来判断逃生时间为 mid 时,所有人能否安全撤离,能则

缩小边界:

  1. 将源点 S 向每个空地连一条容量为 1 的边,表示每个空地初始时有一个人;

  2. 因为每一秒钟只能有一个人移动到门的位置,我们要将每个门拆成 mid 个点,分别表示时刻为第 1 ~ mid 秒的门,并向汇点 T 连一条容量为 1 的边;

  3. 为了简化问题,在二分答案前我们先 BFS 求出每块空地到每个门所需时间,然后将每块空地与对应所需时间的门连边,容量为1;

  4. 那么如果有一些人(人数大于等于1)进入任意一个时刻的门,那么这个时刻的这扇门对应的点必定有 1 的流量流入汇点,但可能会有多个人到达同一个门所需的时间相同,所以我们要让剩下的人等到下一秒再让其中一个人移动到门的位置,所以对于时刻为第 1 ~ mid - 1 秒的门,我们都要向时刻为下一秒的门连一条容量为无穷大的边(因为同一时刻到达门的人可能有任意多,且 mid >= 正确答案时,所有人所代表的流量都将流入汇点 T,所以这里就不多考虑人数了);

  5. 判断如此建图的最大流是否等于总人数(总的空地数),如果一直无法相等则说明所有人无法全部安全撤出,输出“impossible”。

  6. 最后附上一组样例:

    4 5
    XXDXX
    XX.XX
    X...X
    XXDXX

     

附上代码(vector版):

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=20+5;
const int maxt=5e4+5;
const int dx[]={-1,1,0,0},dy[]={0,0,-1,1};
int n,m,E,R,GG[maxn][maxn],dis[100][maxn][maxn],src,des;
char c[maxn][maxn];
bool vis[maxn][maxn];
int q[maxt][2];
const int INF=0x3f3f3f3f;
const int MAX_V=50050;
struct edge{
    int to,cap,rev;
    edge(int _to,int _cap,int _rev):to(_to),cap(_cap),rev(_rev){}
};
vector<edge>G[MAX_V];
int level[MAX_V];
int iter[MAX_V];
void add_edge(int from,int to,int cap)
{
    G[from].push_back(edge(to,cap,G[to].size()));
    G[to].push_back(edge(from,0,G[from].size()-1));
}
void bfs(int s)
{
    memset(level,-1,sizeof(level));
    queue<int>que;
    level[s]=0;
    que.push(s);
    while(!que.empty()){
        int v=que.front();que.pop();
        for(int i=0;i<G[v].size();i++){
            edge &e=G[v][i];
            if(e.cap>0&&level[e.to]<0){
                level[e.to]=level[v]+1;
                que.push(e.to);
            }
        }
    }
}
int dfs(int v,int t,int f)
{
    if(v==t){
        return f;
    }
    for(int &i=iter[v];i<G[v].size();i++){
        edge &e=G[v][i];
        if(e.cap>0&&level[v]<level[e.to]){
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0){
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s,int t)
{
    int flow=0;
    for(;;){
        bfs(s);
        if(level[t]<0){
            return flow;
        }
        memset(iter,0,sizeof(iter));
        int f;
        while((f=dfs(s,t,INF))>0){
            flow+=f;
        }
    }
    return flow;
}
void BFS(int k,int x,int y)
{
    memset(vis,false,sizeof(vis));
    int s=0,t=1;
    vis[x][y]=true;dis[k][x][y]=0;vis[x][y]=true;
    q[s][0]=x;q[s][1]=y;
    while(s<t){
        for(int i=0;i<4;i++){
            int tx=q[s][0]+dx[i],ty=q[s][1]+dy[i];
            if(tx<1||tx>n||ty<1||ty>m||vis[tx][ty]||c[tx][ty]!='.')continue;
 
            dis[k][tx][ty]=dis[k][q[s][0]][q[s][1]]+1;
            q[t][0]=tx;q[t][1]=ty;t++;vis[tx][ty]=true;
        }
        s++;
    }
}
inline bool judge(int mi)
{
    src=0;des=R+E*mi+1;
    for(int i=0;i<=des;i++)G[i].clear();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(c[i][j]=='.')add_edge(src,GG[i][j],1);
        }
    }
    for(int k=1;k<=E;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(c[i][j]=='.'&&dis[k][i][j]<=mi)add_edge(GG[i][j],(k-1)*mi+R+dis[k][i][j],1);
            }
        }
    }
    for(int i=1;i<=E;i++){
        for(int j=1;j<=mi;j++){
            int tmp=(i-1)*mi+R;add_edge(tmp+j,des,1);
            if(j!=mi)add_edge(tmp+j,tmp+j+1,inf);
        }
    }
    return max_flow(src,des)==R;
}
int main()
{
    memset(dis,inf,sizeof(dis));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%s",c[i]+1);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(c[i][j]=='D')BFS(++E,i,j);
            else if(c[i][j]=='.')GG[i][j]=++R;
        }
    }
    int l=0,r=1000;
    while(l+1<r){
        int mid=(l+r)>>1;
        if(judge(mid))r=mid;
        else l=mid;
    }
    if(r==1000)printf("impossible\n");
    else printf("%d\n",r);
    return 0;
}

还有一个不用vector的dinic版:

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=20+5;
const int maxt=5e4+5;
const int dx[]={-1,1,0,0},dy[]={0,0,-1,1};
int n,m,E,R,GG[maxn][maxn],dis[100][maxn][maxn],src,des;
char c[maxn][maxn];
bool vis[maxn][maxn];
int q[maxt][2];
const int INF=0x3f3f3f3f;
const int MAX_V=50050;
int level[MAX_V];
int iter[MAX_V];
struct edge{int v,w,next;}e[1000000];
int head[50050],tot=1;
void add_edge(int u,int v,int w)
{
    e[++tot].v=v;e[tot].w=w;e[tot].next=head[u];head[u]=tot;
    e[++tot].v=u;e[tot].w=0;e[tot].next=head[v];head[v]=tot;
}
void bfs(int s)
{
    for(int i=src;i<=des;i++)iter[i]=head[i],level[i]=-1;
    queue<int>que;
    level[s]=0;
    que.push(s);
    while(!que.empty()){
        int v=que.front();que.pop();
        for(int i=head[v];i;i=e[i].next){
            if(e[i].w>0&&level[e[i].v]<0){
                level[e[i].v]=level[v]+1;
                que.push(e[i].v);
            }
        }
    }
}
int dfs(int v,int t,int f)
{
    if(v==t)return f;
    for(int &i=iter[v];i;i=e[i].next){
        if(e[i].w>0&&level[v]<level[e[i].v]){
            int d=dfs(e[i].v,t,min(f,e[i].w));
            if(d){
                e[i].w-=d;
                e[i^1].w+=d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s,int t)
{
    int flow=0;
    for(;;){
        bfs(s);
        if(level[t]<0)return flow;
        int f;
        while(f=dfs(s,t,INF)>0){
            flow+=f;
        }
    }
    return flow;
}
void BFS(int k,int x,int y)
{
    memset(vis,false,sizeof(vis));
    int s=0,t=1;
    vis[x][y]=true;dis[k][x][y]=0;vis[x][y]=true;
    q[s][0]=x;q[s][1]=y;
    while(s<t){
        for(int i=0;i<4;i++){
            int tx=q[s][0]+dx[i],ty=q[s][1]+dy[i];
            if(tx<1||tx>n||ty<1||ty>m||vis[tx][ty]||c[tx][ty]!='.')continue;

            dis[k][tx][ty]=dis[k][q[s][0]][q[s][1]]+1;
            q[t][0]=tx;q[t][1]=ty;t++;vis[tx][ty]=true;
        }
        s++;
    }
}
inline bool judge(int mi)
{
    src=0;des=R+E*mi+1;
    memset(head,0,sizeof(head));tot=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(c[i][j]=='.')add_edge(src,GG[i][j],1);
        }
    }
    for(int k=1;k<=E;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(c[i][j]=='.'&&dis[k][i][j]<=mi)add_edge(GG[i][j],(k-1)*mi+R+dis[k][i][j],1);
            }
        }
    }
    for(int i=1;i<=E;i++){
        for(int j=1;j<=mi;j++){
            int tmp=(i-1)*mi+R;add_edge(tmp+j,des,1);
            if(j!=mi)add_edge(tmp+j,tmp+j+1,inf);
        }
    }
    return max_flow(src,des)==R;
}
int main()
{
    memset(dis,inf,sizeof(dis));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%s",c[i]+1);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(c[i][j]=='D')BFS(++E,i,j);
            else if(c[i][j]=='.')GG[i][j]=++R;
        }
    }
    int l=0,r=1000;
    while(l+1<r){
        int mid=(l+r)>>1;
        if(judge(mid))r=mid;
        else l=mid;
    }
    if(r==1000)printf("impossible\n");
    else printf("%d\n",r);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值