bzoj 1189 二分+最大流

题目传送门

思路:

  先预处理出每个人到每扇门的时间,用门作为起点进行bfs处理。

  然后二分时间,假设时间为x,将每扇门拆成1到x,x个时间点,表示这扇门有几个时间点是可以出去的。对于一扇门,每个时间点都向后一个时间点建边,表示人在当前时间点到达,可以在下一时间点出去。

  先将s连上所有的空地,流量为1,建立每个空地每个门的对应的时间点流量为1的边,表示这个空地的人会再某一时间点到达这扇门。然后每个门流向t,流量为inf。只要最大流为空地的数量,则代表该时间是可以的,继续向下二分。

#include<bits/stdc++.h>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
int fx[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int dis[100][30][30],n,m;
char s[30][30];
int ed[1000],g[30][30],cnt,sum,st,t;
const int maxn=40010;
const int inf=0x3f3f3f3f;
int head[maxn],tot=0;
struct edge{
    int to,f,Next;
}a[maxn<<1];
void addv(int u,int v,int w){
    a[++tot].to=v,a[tot].f=w,a[tot].Next=head[u],head[u]=tot;
    a[++tot].to=u,a[tot].f=0,a[tot].Next=head[v],head[v]=tot;
}
inline void bfs(int id){
    queue<int >q;
    dis[id][ed[id]/m][ed[id]%m]=0;
    q.push(ed[id]);
    while(!q.empty()){
        int u=q.front();
        int x=u/m,y=u%m;
        q.pop();
        for(int i=0;i<4;i++){
            int xx=x+fx[i][0],yy=y+fx[i][1];
            if(xx<0||xx>=n||yy<0||yy>=m||s[xx][yy]!='.')continue;
            if (dis[id][xx][yy]>dis[id][x][y]+1)
                dis[id][xx][yy]=dis[id][x][y]+1,q.push(xx*m+yy);

        }
    }
}
int deep[maxn];
bool vis[maxn];
inline void find(){
    CLR(deep,0);
    queue<int >q;
    vis[st]=1;
    q.push(st);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=head[u];i!=-1;i=a[i].Next)
        {
            if(a[i].f && !vis[a[i].to]){
                q.push(a[i].to);
                deep[a[i].to]=deep[u]+1;
                vis[a[i].to]=1;
            }
        }
    }
}
int dfs(int u,int delta){
    if(u==t)return delta;
    int ret=0;
    for(int i=head[u];delta&&i!=-1;i=a[i].Next){
        if(a[i].f&&deep[a[i].to]==deep[u]+1){
            int dd=dfs(a[i].to,min(a[i].f,delta));
            a[i].f-=dd;
            a[i^1].f+=dd;
            delta-=dd;
            ret+=dd;
        }
    }
    return ret;
}
inline bool check(int x){
    st=0,t=maxn-2,tot=1;
    CLR(head,-1);
    for(int i=1;i<=sum;i++)addv(st,i,1);
    for(int k=1;k<=cnt;k++)
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
                if(s[i][j]=='.'&&dis[k][i][j]<=x)
                    addv(g[i][j],sum+(k-1)*x+dis[k][i][j],1);
    
    for (int i=1; i<=cnt; i++)
        for (int j=1; j<=x; j++) {
            int tmp=(i-1)*x+sum;
            addv(tmp+j,t,1);
            if (j<x) addv(tmp+j,tmp+j+1,inf);
        }
    int ret=0;
    while(1){
        CLR(vis,0);
        find();
        if(!vis[t]){
            return ret==sum;
        }
        ret+=dfs(st,inf);
    }
}
int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        scanf("%s",s[i]);
    }
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(s[i][j]=='D'){
                ed[++cnt]=i*m+j;
            }
            if(s[i][j]=='.')g[i][j]=++sum;
        }
    }
    CLR(dis,0x3f);
    for(int i=1;i<=cnt;i++)
    {
        bfs(i);
    }
    int l=1,r=n*m,mid;
    int ans;
    if(!check(r)){
        puts("impossible");
        return 0;
    }
    while(l<=r){
        
        mid=(l+r)>>1;//printf("l:%d  r:%d  mid:%d\n",l,r,mid);
        if(check(mid)){
        ans=mid,r=mid-1;
    //    printf("ans:%d\n",ans);
    }
        else l=mid+1;
    }
    cout<<ans<<endl;
    
}

1189: [HNOI2007]紧急疏散evacuate

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 3825  Solved: 1118
[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
posted @ 2018-11-26 20:39  光芒万丈小太阳  阅读(136)  评论(0编辑  收藏  举报