bzoj千题计划132:bzoj1189: [HNOI2007]紧急疏散evacuate

Posted 日拱一卒 功不唐捐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj千题计划132:bzoj1189: [HNOI2007]紧急疏散evacuate相关的知识,希望对你有一定的参考价值。

http://www.lydsy.com/JudgeOnline/problem.php?id=1189

 

二分答案

源点向人连边,流量为1

门拆为mid个点,同一个门的第j个点向第j+1个点连边,流量为inf

若第i个人第k秒到达第j个门,第i个人向第j个门拆出的第k个点连边,流量为1

所有门向汇点连边,流量为1

 

用ISAP写的,真快,也真TM长

 

#include<queue>
#include<cstdio>
#include<algorithm>
#include<cstring>
 
using namespace std;
 
#define N 36001
#define M 320001
 
const int inf=500;
 
int mp[21][21];
 
int n,m,cnt;
char s[21];
 
int sum[401],tim[N][81],wh[N][81];
 
int sum_door,doorx[81],doory[81],id_door[21][21];
 
int dis[21][21];
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
 
int tot;
int front[N],nxt[M<<1],to[M<<1],val[M<<1],from[M<<1];
int lev[N],num[N];
int path[N];
int cur[N];
 
int src,decc;
 
struct node
{
    int x,y;
}nw,nt;
 
bool inmap(int x,int y)
{
    if(x<=0 || x>n || y<=0 || y>m) return false;
    return mp[x][y];
}
 
void prebfs(int x,int y)
{
    queue<node>q;
    nw.x=x; nw.y=y;
    q.push(nw);
    while(!q.empty())
    {
        nw=q.front();
        q.pop();
        for(int i=0;i<4;++i)
        {
            nt.x=nw.x+dx[i];
            nt.y=nw.y+dy[i];
            if(!inmap(nt.x,nt.y)) continue;
            if(!dis[nt.x][nt.y]) 
            {
                dis[nt.x][nt.y]=dis[nw.x][nw.y]+1;
                if(mp[nt.x][nt.y]==2) continue;
                q.push(nt);
            }
        }
    }
}
 
void add(int u,int v,int w)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; from[tot]=u; val[tot]=w;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; from[tot]=v; val[tot]=0;
}
 
void rebuild(int mid)
{
    tot=1;
    memset(front,0,sizeof(front));
    decc=mid*sum_door+cnt+1;
    for(int i=1;i<=cnt;++i) add(src,i,1);
    for(int i=1;i<=sum_door;++i)
        for(int j=1;j<=mid;++j)
        {
            add((i-1)*mid+j+cnt,decc,1);
            if(j!=mid) add((i-1)*mid+j+cnt,(i-1)*mid+j+cnt+1,inf);
        }
    for(int i=1;i<=cnt;++i)
        for(int j=1;j<=sum[i];++j)
            if(tim[i][j]<=mid) add(i,(wh[i][j]-1)*mid+tim[i][j]+cnt,1);
}
 
bool bfs()
{
    queue<int>q;
    for(int i=src;i<=decc;++i) lev[i]=decc;
    q.push(decc);
    lev[decc]=0;
    int now,t;
    while(!q.empty())
    {
        now=q.front();
        q.pop();
        for(int i=front[now];i;i=nxt[i])
        {
            t=to[i];
            if(lev[t]==decc && val[i^1]) 
            {
                lev[t]=lev[now]+1;
                q.push(t);
            }
        }
    }
    return lev[src]!=decc;
}
 
int augment()
{
    int now=decc,flow=inf;
    int i;
    while(now!=src)
    {
        i=path[now];
        flow=min(flow,val[i]);
        now=from[i];
    }
    now=decc;
    while(now!=src)
    {
        i=path[now];
        val[i]-=flow;
        val[i^1]+=flow;
        now=from[i];
    }
    return flow;
}
 
int isap()
{
    int flow=0;
    if(!bfs()) return 0;
    memset(num,0,sizeof(num));
    for(int i=src;i<=decc;++i) num[lev[i]]++,cur[i]=front[i];
    int now=src,t;
    while(lev[src]<=decc)
    {
        if(now==decc)
        {
            flow+=augment();
            now=src;
        }
        bool advanced=false;
        for(int i=cur[now];i;i=nxt[i])
        {
            t=to[i];
            if(lev[t]==lev[now]-1 && val[i])
            {
                advanced=true;
                path[t]=i;
                cur[now]=i;
                now=t;
                break;
            }
        }
        if(!advanced)
        {
            int mi=decc;
            for(int i=front[now];i;i=nxt[i])
                if(val[i]) mi=min(mi,lev[to[i]]);
            if(!--num[lev[now]]) break;
            num[lev[now]=mi+1]++;
            cur[now]=front[now];
            if(now!=src) now=from[path[now]];
        }
    }
    return flow;
}
 
bool check(int mid)
{
    rebuild(mid);
    return isap()==cnt;
}
 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",s+1);
        for(int j=1;j<=m;++j)
        {
            if(s[j]==.) mp[i][j]=1;
            else if(s[j]==D) 
            {
                sum_door++;
                doorx[sum_door]=i;
                doory[sum_door]=j;
                mp[i][j]=2;
                id_door[i][j]=sum_door;
            }
        }
    }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            if(mp[i][j]!=1) continue;
            cnt++;
            memset(dis,0,sizeof(dis));
            prebfs(i,j);
            for(int i=1;i<=sum_door;++i)
            {
                if(dis[doorx[i]][doory[i]]) 
                {
                    tim[cnt][++sum[cnt]]=dis[doorx[i]][doory[i]];
                    wh[cnt][sum[cnt]]=id_door[doorx[i]][doory[i]];
                }
            }
        }
    int l=0,r=450,mid,ans=-1;
    while(l<=r)
    {
        mid=l+r>>1;
        if(check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    if(ans==-1) printf("impossible");
    else printf("%d",ans);
    return 0;
} 

 

1189: [HNOI2007]紧急疏散evacuate

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 3362  Solved: 994
[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

以上是关于bzoj千题计划132:bzoj1189: [HNOI2007]紧急疏散evacuate的主要内容,如果未能解决你的问题,请参考以下文章

bzoj千题计划197:bzoj4247: 挂饰

bzoj千题计划118:bzoj1028: [JSOI2007]麻将

bzoj千题计划165:bzoj5127: 数据校验

bzoj千题计划144:bzoj1176: [Balkan2007]Mokia

bzoj千题计划177:bzoj1858: [Scoi2010]序列操作

bzoj千题计划142:bzoj3144: [Hnoi2013]切糕