【网络流】hdu1733 Escape

题意:(starfall512.com/?p=320)教室里有很多人,有若干个出口,问所有人最少需要多少时间全部走到教室外面。
难度:4
题解:如果人在出口的位置,就算出去;每个位置每次只能站一个人;先BFS判断是否全部人都能走出去(排除无解的情况)。分层建图,每层代表一个时间点。比如第n层图,首先每个位置的点拆点,两点之间容量为1(为了满足每个点最多一个人)对于每一个位置(x,y),向四个方向(xx,yy),如果不越界并且不是‘#’(wall),就从n-1层的x,y位置建边到n层的xx,yy点,n-1层的(x,y)也要连向n层的(x,y)点,每层建图后计算最大流,最大流就是那个时间点上能够出教室的人数。出教室的总人数需要累加,用来判断跳出循环。可以二分最大层数,也可以一层层累加上去。
#include <cstdio>
using namespace std;
const int mm = 11111111;
const int mn = 111111;
const int oo = 1000000000;
const int dx[4] = {0,1,0,-1};
const int dy[4] = {1,0,-1,0};
int node,src,dest,edge,ret;
int reach[mm],flow[mm],next[mm];
int head[mn],work[mn],dis[mn],q[mn];
char map[22][22];
int mark[22][22],d[22][22],qx[mn],qy[mn];
int i,j,k,n,m,x,y,t,ans,num;
inline int min(int a,int b) {
    return a<b?a:b;    
} 
inline void addedge(int u,int v,int c) {
    reach[edge]=v;flow[edge]=c;next[edge]=head[u];head[u]=edge++;
    reach[edge]=u;flow[edge]=0;next[edge]=head[v];head[v]=edge++;    
}
inline void makegraph(int c,int dd) {  //分层图,每次加进去一层图,从小到大枚举 
    int i,j,k,w=node-2;
    while(dd--) head[node++]=-1;
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            if(mark[i][j]) {
                addedge(w+mark[i][j],w+mark[i][j]+t,1);
                if(!c && map[i][j]=='X') addedge(src,w+mark[i][j],1);
                if(c) {
                    addedge(w-t+mark[i][j],w+mark[i][j],1);    
                    if(map[i][j]=='@') addedge(w+mark[i][j]+t,dest,1);
                    for(k=0;k<4;k++) {
                        x=i+dx[k];
                        y=j+dy[k];
                        if(x>0&&x<=n&&y>0&&y<=m&&mark[x][y])
                            addedge(w+mark[x][y]-t,w+mark[i][j],1);    
                    }
                }
            }
}
bool Dinic_bfs() {
    int i,u,v,l,r=0;
    for(i=0;i<node;i++) dis[i]=-1;
    dis[q[r++]=src] = 0;
    for(l=0;l<r;l++) 
        for(i=head[u=q[l]];i>=0;i=next[i]) 
            if(flow[i] && dis[v=reach[i]]<0) {
                dis[q[r++]=v] = dis[u] + 1;
                if(v==dest) return 1;    
            }    
    return 0;
}
int Dinic_dfs(int u,int exp) {
    if(u==dest) return exp;
    for(int &i=work[u],v,tmp;i>=0;i=next[i])   
        if(flow[i]&&dis[v=reach[i]]==dis[u]+1&&(tmp=Dinic_dfs(v,min(exp,flow[i]))>0)) {
            flow[i]-=tmp;
            flow[i^1]+=tmp;
            return tmp;    
        } 
    return 0;
}
int Dinic_flow() {
    int i,delta;
    while(Dinic_bfs()) {
        for(i=0;i<node;i++) work[i]=head[i];
        while(delta=Dinic_dfs(src,oo)) ret += delta;    
    }    
    return ret;
}
void bfs(int r) {
    int i,l;
    for(l=0;l<r;l++)
        for(i=0;i<4;i++) {
            x=qx[l]+dx[i];
            y=qy[l]+dy[i];
            if(x>0&&x<=n&&y>0&&y<=m&&map[x][y]!='#'&&d[x][y]<0)
                qx[r]=x,qy[r]=y,d[x][y]=d[qx[l]][qy[l]]+1,++r;    
        }
}
bool ok() {
       for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(map[i][j]=='X' && d[i][j]<0) return 0;
       return 1; 
}
int main() {
    while(~scanf("%d%d",&n,&m)) {
        for(t=0,i=1;i<=n;i++)
            for(getchar(),j=1;j<=m;j++) {
                map[i][j] = getchar();
                mark[i][j] = 0;
                if(map[i][j]!='#') mark[i][j]=(++t) + 1; //t指不是障碍物的个数,mark[i][j]指不是障碍物的编号从2到t+1    
            }        
        for(k=0,i=1;i<=n;i++)
            for(j=1;j<=m;j++)
                if(map[i][j]=='@') qx[k]=i,qy[k]=j,d[i][j]=0,++k;//把每个出口的坐标储存在qx[],qy[]中,k表示有几个出口
                else d[i][j]=-1;//不是出口的点的d[i][j]全都设为-1
        bfs(k);
        if(!ok()) {
            printf("-1\n");
            continue;    
        } 
        for(num=0,i=1;i<=n;i++)
            for(j=1;j<=m;j++)
                if(map[i][j]=='X') ++num;
        ans=ret=0;
        head[0]=head[1]=-1;
        node=2,src=0,dest=1;
        makegraph(0,t*2);
        while(Dinic_flow()<num) makegraph(1,t*2),++ans;
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值