hdu 1733 Escape(分层最大流)

Escape

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1396    Accepted Submission(s): 399


Problem Description
Loneknight hates attending class very much. Every time he is attending class, when he feel tiresome with the class, he start planning the shortest path he can escape from the classroom. Therefore, he can escape from classroom so quickly that he is always the first one escape from the classroom after the class finishes. He is very proud of this fact. 

One day, loneknight is day dreaming in class, and planning the shortest path for escaping. Suddently, an issue come into his mind, if everyone in the classroom want to escape as soon as possible just as loneknight, what will happend? As a kind man, loneknight want to plan a strategy that will let everyone escape from the classroom with minimum time consume. But after dozens of minutes of consideration, loneknight find this problem so difficult for him.

Now, as the best friend of him, please design a algorithm to solve this problem for him. Note that, at every time unit, everyone can move seperately up, down, left, right one unit, or stay in the old position. In addtion, no one can move to a wall or out of the room, the only way to escape is go through a gate. Moreover, at every time unit, a position can only contain a person, including gates. That means if in time t, a person escape thourgh gate g, no one can go into g in time t, but can go into g in t + 1. Now, it's your job to calculate the minimum time required to escape for everyone.
 

Input
The input consists of several test cases. Each test case start with a line containing two number, n, m (1 < n, m <= 16), the rows and the columns of the room. Then n lines follow, each contain exact m characters, representing th type of unitin it. (. for empty place, X for human, # for wall, @ for gate). Input is end with EOF.
 

Output
You have to print the shortest time needed for everyone escape from the roomin a single line for each case. (-1 if impossible)
 

Sample Input
  
  
4 4 .@.. .X.. .... .... 4 4 .@.. .XX. .XX. ..@. 4 4 .@.. .#.. #X#. .#..
 

Sample Output
  
  
1 2 -1
 

Source

题意:有一个n*m的矩阵,上面X代表人,@代表出口,#代表障碍物,现在要使所有人都从出口出去,问最少需要的时间,只能走上下左右,走一步1个单位时间

同一个位置同一个时间只能有一个人(也就是说多个人是同时走的,出口处同一个时间也只能有一个人)

思路:因为必须确保到同一个时间同一个地点,所以枚举时间,用分层最大流去做,每次到下一个时间就把当前位置向五个方向(保持不动或者上下左右)连边,最后判断满流即可

如果当前时间还不足以让所有人离开,那么把所有的人-当前增流后可以离开的人,最后当Dinic==剩余的人的时候就是最少的时间了

为什么这里可以达到同一个时间同一个地点只有一个人的效果呢?

这里我们先用了BFS去判断所有的点是否都可以到达终点,如果有一个点因为障碍物到达不了那么输出-1

然后对于某一个点,在某一个确定的时间,我们把这个点拆成两个点,连接一条容量为1的边,这样无论入度有多少,出度都只能有1了。

注意结果要time-1~

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
#define N 30000
#define INF 99999999
struct Edge
{
    int v,next,cap;
} edge[2000000];
struct Node
{
    int x,y;
};
int cnt,head[N],sum,vis[20][20],d[N];
int s,t;
int n,m;
int dir[4][2]= {-1,0,1,0,0,-1,0,1};
char ma[20][20];
int judge(int u,int v)
{
    memset(vis,0,sizeof(vis));
    Node a,temp;
    a.x=u,a.y=v;
    queue<Node>q;
    q.push(a);
    vis[u][v]=1;
    while(!q.empty())
    {
        Node now=q.front();
        q.pop();
        for(int i=0; i<4; i++)
        {
            int x=now.x+dir[i][0],y=now.y+dir[i][1];
            if(x<1||x>n||y<1||y>m||ma[x][y]=='#') continue;
            if(!vis[x][y])
            {
                if(ma[x][y]=='@') return 1;
                vis[x][y]=1;
                temp.x=x,temp.y=y;
                q.push(temp);
            }
        }
    }
    return 0;
}
int ok()
{
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
            if(ma[i][j]=='X'&&!judge(i,j))
                return 0;
    return 1;
}
void addedge(int u,int v,int cap)
{
    edge[cnt].v=v;
    edge[cnt].cap=cap;
    edge[cnt].next=head[u];
    head[u]=cnt++;

    edge[cnt].v=u;
    edge[cnt].cap=0;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}
void init()
{
    cnt=sum=0;
    memset(head,-1,sizeof(head));
    s=0,t=1;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
        {
            if(ma[i][j]=='X')
            {
                sum++;
                addedge(s,(i-1)*m+j+1,1);
            }
        }
}
int bfs()
{
    memset(d,-1,sizeof(d));
    d[s]=0;
    queue<int>q;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u]; i!=-1; i=edge[i].next)
        {
            int v=edge[i].v,cap=edge[i].cap;
            if(d[v]==-1&&cap>0)
            {
                d[v]=d[u]+1;
                q.push(v);
            }
        }
    }
    return d[t]!=-1;
}
int dfs(int ss,int tt,int f)
{
    if(ss==tt||f==0) return f;
    int flow=0;
    for(int i=head[ss]; i!=-1; i=edge[i].next)
    {
        int v=edge[i].v,cap=edge[i].cap;
        if(d[v]==d[ss]+1&&cap>0)
        {
            int x=min(f-flow,cap);
            x=dfs(v,t,x);
            flow+=x;
            edge[i].cap-=x;
            edge[i^1].cap+=x;
        }
    }
    if(!flow) d[ss]=-2;
    return flow;
}
int Dinic()
{
    int flow=0,f;
    while(bfs())
    {
        while(f=dfs(s,t,INF))
            flow+=f;
    }
    return flow;
}
int solve(int ti)
{
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            if(ma[i][j]=='#') continue;
            int u=(ti-1)*n*m*2+(i-1)*m+j+1;
            int out=u+n*m;
            printf("%d %d\n",u,out);
            addedge(u,out,1);
            addedge(out,out+n*m,1);
            for(int k=0; k<4; k++)
            {
                int x=i+dir[k][0],y=j+dir[k][1];
                if(x<1||x>n||y<1||y>m||ma[x][y]=='#') continue;
                int v=(x-1)*m+y;
                v=ti*n*m*2+v+1;
                addedge(out,v,1);
            }
        }
    }
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
            if(ma[i][j]=='@')
                addedge((ti-1)*n*m*2+n*m+(i-1)*m+j+1,t,1);
    int num=Dinic();
    if(num==sum) return 1;
    sum-=num;
    return 0;
}
int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        for(int i=1; i<=n; i++)
            scanf("%s",ma[i]+1);
        if(!ok())
        {
            printf("-1\n");
            continue;
        }
        init();
        for(int i=1;; i++)
        {
            if(solve(i))
            {
                printf("%d\n",i-1);
                break;
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值