• 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
    ",l,r,mid);
            if(check(mid)){
            ans=mid,r=mid-1;
        //    printf("ans:%d
    ",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
  • 相关阅读:
    敏捷开发
    开撕队-软件需求规格说明书
    开撕队前来问候
    Four-operations: 使用node.js实现四则运算程序
    wordcount
    编码的故事转载2018-02-28更新
    MySQL学习笔记2018-02-07更新
    Linux各文件及目录说明2018-03-01更新
    Linux安全运维笔记2018-03-01更新
    jQuery实现商品五星评价
  • 原文地址:https://www.cnblogs.com/mountaink/p/10022786.html
Copyright © 2020-2023  润新知