bzoj 1189(拆点最大流)

1189: [HNOI2007]紧急疏散evacuate

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 1637   Solved: 557
[ Submit][ Status][ Discuss]

Description

发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域。每个格子如果是'.',那么表示这是一块空地;如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间。已知门一定在房间的边界上,并且边界上不会有空地。最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动。疏散开始后,每块空地上就没有人数限制了(也就是说每块空地可以同时站无数个人)。但是,由于门很窄,每一秒钟只能有一个人移动到门的位置,一旦移动到门的位置,就表示他已经安全撤离了。现在的问题是:如果希望所有的人安全撤离,最短需要多少时间?或者告知根本不可能。

Input

输入文件第一行是由空格隔开的一对正整数N与M,3<=N <=20,3<=M<=20,以下N行M列描述一个N M的矩阵。其中的元素可为字符'.'、'X'和'D',且字符间无空格。

Output

只有一个整数K,表示让所有人安全撤离的最短时间,如果不可能撤离,那么输出'impossible'(不包括引号)。

Sample Input

5 5
XXXXX
X...D
XX.XX
X..XX
XXDXX

Sample Output

3


解题思路:二分答案,然后判断。对每扇门拆成400个以时间为依据的点。然后最大流。


  1. #include <queue>  
  2. #include <cstdio>  
  3. #include <cstring>  
  4. #include <iostream>  
  5. #include <algorithm>  
  6. #define P 25  
  7. #define N 500  
  8. #define M 300000  
  9. #define R 500  
  10. #define inf 0x3f3f3f3f  
  11. using namespace std;  
  12. const int dx[]={0,0,1,-1};  
  13. const int dy[]={1,-1,0,0};  
  14. struct KSD  
  15. {  
  16.     int v,len,next;  
  17.     int flow;  
  18.     void init(){len=flow;}  
  19. }e[M];  
  20. int head[50000],cnt;  
  21. inline void add(int u,int v,int len)  
  22. {  
  23.     e[++cnt].v=v;  
  24.     e[cnt].len=e[cnt].flow=len;  
  25.     e[cnt].next=head[u];  
  26.     head[u]=cnt;  
  27. }  
  28. inline void Add(int u,int v,int len){add(u,v,len),add(v,u,0);}  
  29. int s,t,d[50000];  
  30. queue<int>q;  
  31. bool bfs()  
  32. {  
  33.     memset(d,0,sizeof d);  
  34.     while(!q.empty())q.pop();  
  35.   
  36.     int i,u,v;  
  37.     q.push(s),d[s]=1;  
  38.     while(!q.empty())  
  39.     {  
  40.         u=q.front(),q.pop();  
  41.         for(i=head[u];i;i=e[i].next)  
  42.         {  
  43.             if(!d[v=e[i].v]&&e[i].len)  
  44.             {  
  45.                 d[v]=d[u]+1;  
  46.                 if(v==t)return 1;  
  47.                 q.push(v);  
  48.             }  
  49.         }  
  50.     }  
  51.     return 0;  
  52. }  
  53. int dinic(int x,int flow)  
  54. {  
  55.     if(x==t)return flow;  
  56.     int remain=flow,i,v,k;  
  57.     for(i=head[x];i&&remain;i=e[i].next)  
  58.     {  
  59.         if(e[i].len&&d[v=e[i].v]==d[x]+1)  
  60.         {  
  61.             k=dinic(v,min(remain,e[i].len));  
  62.             if(!k)d[v]=0;  
  63.             e[i].len-=k,e[i^1].len+=k;  
  64.             remain-=k;  
  65.         }  
  66.     }  
  67.     return flow-remain;  
  68. }  
  69.   
  70. int blank[N],blanks;  
  71. int door[N],doors;  
  72. int point[N][R+5],rec[N][R+5];  
  73. bool is_door[N];  
  74. void build(int mid)  
  75. {  
  76.     int i,j;  
  77.     for(i=2;i<=cnt;i++)e[i].init();  
  78.     for(i=1;i<=doors;i++)for(j=mid+1;j<=R;j++)e[rec[i][j]].len=0;  
  79.     return ;  
  80. }  
  81. int n,m,id[P][P];  
  82. int map[N][N];  
  83. char src[P][P];  
  84. void Build(int &l,int &r)  
  85. {  
  86.     int i,j,k;  
  87.     int tx,ty;  
  88.     scanf("%d%d",&n,&m);  
  89.     for(i=1;i<=n;i++)scanf("%s",src[i]+1);  
  90.     for(i=1;i<=n;i++)for(j=1;j<=m;j++)  
  91.     {  
  92.         if(src[i][j]=='.')blank[++blanks]=id[i][j]=++cnt;  
  93.         else if(src[i][j]=='D')  
  94.         {  
  95.             door[++doors]=id[i][j]=++cnt;  
  96.             is_door[cnt]=true;  
  97.         }  
  98.     }     
  99.     memset(map,0x3f,sizeof map);  
  100.     for(i=1;i<=cnt;i++)map[i][i]=0;  
  101.     for(i=1;i<=n;i++)for(j=1;j<=m;j++)if(id[i][j])  
  102.         for(k=0;k<4;k++)if(id[tx=i+dx[k]][ty=j+dy[k]])  
  103.             map[id[i][j]][id[tx][ty]]=1;  
  104.     for(k=1;k<=cnt;k++)  
  105.     {  
  106.         if(is_door[k])continue;  
  107.         for(i=1;i<=cnt;i++)for(j=1;j<=cnt;j++)map[i][j]=min(map[i][j],map[i][k]+map[k][j]);  
  108.     }  
  109.     cnt=blanks;  
  110.     for(i=1;i<=doors;i++)for(j=1;j<=R;j++)point[i][j]=++cnt;  
  111.     s=0,t=cnt+1,cnt=1;  
  112.     for(i=1;i<=blanks;i++)  
  113.     {  
  114.         Add(s,i,1); // 500  
  115.         int res=inf;  
  116.         for(j=1;j<=doors;j++)  
  117.             res=min(res,map[blank[i]][door[j]]);  
  118.         l=max(l,res);  
  119.     }  
  120.     for(i=1;i<=doors;i++)  
  121.     {  
  122.         int I=door[i],J;  
  123.         for(j=1;j<=blanks;j++)if(map[I][J=blank[j]]<inf)  
  124.             Add(j,point[i][map[I][J]],1);  
  125.     }  
  126.     for(i=1;i<=doors;i++)for(j=1;j<R;j++)Add(point[i][j],point[i][j+1],inf);  
  127.   
  128.     for(i=1;i<=doors;i++)for(j=1;j<=R;j++) // 80*500  
  129.     {  
  130.         Add(point[i][j],t,1);  
  131.         rec[i][j]=cnt-1;  
  132.     }  
  133.     return ;  
  134. }  
  135. bool check(int mid)  
  136. {  
  137.     build(mid);  
  138.     int maxflow=0;  
  139.     while(bfs())maxflow+=dinic(s,inf);  
  140.     if(maxflow==blanks)return 1;  
  141.     return 0;  
  142. }  
  143. int main()  
  144. {  
  145.     int l=0,r=R,mid,ans=inf;  
  146.     Build(l,r);  
  147.     while(l<=r)  
  148.     {  
  149.         if(r-l<=3)  
  150.         {  
  151.             for(int i=l;i<=r;i++)if(check(i)){ans=i;break;}  
  152.             break;  
  153.         }  
  154.         mid=l+r>>1;  
  155.         if(check(mid))r=mid;  
  156.         else l=mid+1;  
  157.     }  
  158.     if(ans==inf)puts("impossible");  
  159.     else printf("%d\n",ans);  
  160.     return 0;  
  161. }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值