BZOJ 1189 HNOI2007 紧急疏散evacuate 二分答案+最大流

题目大意:给定一个m*n的地图,每个点有可能是空地、墙或者出口,每个空地初始站着一个人,每一时刻可以向周围走1格,门每一时刻只能通过一个人,求最短多少时间后所有人可以撤离

首先从每个出口出发开始广搜,得到每个空地到所有出口的距离

然后二分答案,每次建图如下:

从源点向每个空地一条流量为1的边

如果一个空地能在规定时间到达某个出口,就从这个空地出发向该出口链接一条流量为1的边

每个出口向汇点连接一条流量为时间的边

然后跑最大流验证即可 注意图有不连通的情况 所以广搜要清初值(这个没人会忘吧QAQ 但是我卡了一晚上……)

那么问题来了:

会不会有这样的情况,规定时间为2,而两个人同时在时间2到达门口,于是堵住了,但是跑出来的的答案却是2呢?

……很不幸地告诉你 会

比如说这组样例:

这个样例的答案是3 但是这样建图跑出来是2

这幅图中间下方的人有两个选择:从上面逃生或从下面逃生

如果从下面逃生 那么左右两个人到上面的距离都是3 2时间内无法逃出

如果从上面逃生 那么两侧的人就会同时在时间2到达下侧出口 于是就愉快地卡住了~~

无视吧。。。不考虑这个这题还是能做的。。。。。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 30
#define S abcd(0,0)
#define T abcd(29,29)
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int> abcd;
template<typename Temp>class Reader{
    private:
        Temp xx[M][M];
    public:
        Temp& operator [] (abcd x)
        {
            return xx[x.first][x.second];
        }
        Temp* operator [] (int x)
        {
            return xx[x];
        }
        inline void Clear(int x)
        {
            memset(xx,x,sizeof xx);
        }
};
struct edge{
    abcd to;
    int f,next;
}table[100100];
Reader<int>head;int tot=1;
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
int m,n,cnt,per;
char s[M];
abcd exi[M<<2];
Reader<int>f[M<<2];
Reader<int>map;
Reader<int>dpt;
void Add(abcd x,abcd y,int z)
{
    table[++tot].to=y;
    table[tot].f=z;
    table[tot].next=head[x];
    head[x]=tot;
}
void Link(abcd x,abcd y,int z)
{
    Add(x,y,z);
    Add(y,x,0);
}
void BFS(abcd start,Reader<int>&f)
{
    static abcd q[1<<16];
    int i,r=0,h=0;
    f.Clear(0x3f);q[++r]=start;f[start]=0;
    while(r!=h)
    {
        abcd x=q[++h];
        for(i=0;i<4;i++)
        {
            abcd y(x.first+dx[i],x.second+dy[i]);
            if(y.first<=0||y.second<=0||y.first>m||y.second>n)
                continue;
            if(map[y]||f[y]!=0x3f3f3f3f) continue;
            f[y]=f[x]+1;
            q[++r]=y;
        }
    }
}
bool BFS()
{
    static abcd q[1<<16];
    int i,r=0,h=0;
    dpt.Clear(-1);
    q[++r]=S;dpt[S]=1;
    while(r!=h)
    {
        abcd x=q[++h];
        for(i=head[x];i;i=table[i].next)
            if(table[i].f&&!~dpt[table[i].to])
            {
                dpt[table[i].to]=dpt[x]+1;
                q[++r]=table[i].to;
                if(table[i].to==T)
                    return true;
            }
    }
    return false;
}
int Dinic(abcd x,int flow)
{
    int i,left=flow;
    if(x==T) return flow;
    for(i=head[x];i&&left;i=table[i].next)
        if(table[i].f&&dpt[table[i].to]==dpt[x]+1)
        {
            int temp=Dinic(table[i].to,min(left,table[i].f) );
            if(!temp) dpt[table[i].to]=-1;
            left-=temp;
            table[i].f-=temp;
            table[i^1].f+=temp;
        }
    return flow-left;
}
bool Judge(int x)
{
    int i,j,k,re=0;
    head.Clear(0),tot=1;
    for(i=1;i<=m;i++)
        for(j=1;j<=n;j++)
            if(map[i][j]==0)
            {
                Link(S,abcd(i,j),1);
                for(k=1;k<=cnt;k++)
                    if(f[k][i][j]<=x)
                        Link(abcd(i,j),exi[k],1);
            }
    for(k=1;k<=cnt;k++)
        Link(exi[k],T,x);
    while( BFS() )
        re+=Dinic(S,INF);
    return re==per;
}
int Bisection()
{
    int l=0,r=m*n;
    while(l+1<r)
    {
        int mid=l+r>>1;
        if( Judge(mid) )
            r=mid;
        else
            l=mid;
    }
    if( Judge(l) )
        return l;
    return r;
}
int main()
{

    //freopen("evacuate.in","r",stdin);
    //freopen("evacuate.out","w",stdout);

    int i,j;
    cin>>m>>n;
    for(i=1;i<=m;i++)
    {
        scanf("%s",s+1);
        for(j=1;j<=n;j++)
            switch(s[j])
            {
                case 'D':
                    exi[++cnt]=abcd(i,j);
                case 'X':
                    map[i][j]=1;
                    break;
                case '.':
                    map[i][j]=0;
                    ++per;
                    break;
            }
    }
    for(i=1;i<=cnt;i++)
        BFS(exi[i],f[i]);
    int ans=Bisection();
    if(ans==m*n) puts("impossible");
    else cout<<ans<<endl;
    return 0;
}
时间: 2024-08-02 10:30:03

BZOJ 1189 HNOI2007 紧急疏散evacuate 二分答案+最大流的相关文章

BZOJ 1189: [HNOI2007]紧急疏散evacuate( BFS + 二分答案 + 匈牙利 )

我们可以BFS出每个出口到每个人的最短距离, 然后二分答案, 假设当前答案为m, 把一个出口拆成m个表示m个时间, 点u到出口v的距离为d, 那么u->v的[d, m]所有点连边, 然后跑匈牙利去check就行了...其实这道题挺好想但是码量还是挺大的.... ----------------------------------------------------------------------------- #include<cstdio> #include<cstring&

BZOJ 1189 HNOI 2007 紧急疏散 evacuate 二分答案 最大流

题目大意:紧急疏散.有一张地图,'.'表示人,'D'表示门,人需要走曼哈顿距离的单位时间才1能到达门.一个门一个时刻只能通过一个人.求多长时间能疏散完毕. 思路:二分答案+最大流满流判定.先BFS处理出每个人与门的距离.二分最小时间,然后连边.S向每个人连流量为1的边,每个人向二分的时间之内能到达的门连流量为1的边.每个门向T连流量为t的边.然后最大流判定是否满流. (数组大小我是瞎开的,写代码的时候要算好了在开!) CODE: #include <queue> #include <cs

BZOJ 1189 [HNOI2007]紧急疏散evacuate

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

【BZOJ1189】【HNOI2007】紧急疏散evacuate 二分答案+最大流check

#include <stdio.h> int main() { puts("转载请注明出处"); puts("地址:blog.csdn.net/vmurder/article/details/43666807"); } 题解: 首先floyd或者bfs求出每个'.'到各个'D'的最短路 然后注意一个点不能经过一个门去另一个门,所以可以看到我的floyd略有一点点点点不同... 然后这个时间限制可以转化为对每个门的拆点,可以证明拆400个就够了. 然后分别

BZOJ1189: [HNOI2007]紧急疏散evacuate 二分+最大流

1189: [HNOI2007]紧急疏散evacuate Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1132  Solved: 412[Submit][Status][Discuss] Description 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间.已知门一定在房间的边界上,并且

BZOJ 1305 CQOI2009 dance跳舞 二分答案+最大流

题目大意:给定n个男生和n个女生,一些互相喜欢而一些不.举行几次舞会,每次舞会要配成n对.不能有同样的组合出现.每一个人仅仅能与不喜欢的人跳k次舞,求最多举行几次舞会 将一个人拆成两个点.点1向点2连一条流量为k的边.两个人若互相喜欢则点1之间连边,不喜欢则点2之间连边 对于每个要验证的x值 将每个人的点1向源或汇连一条流量为x的边 然后二分答案跑最大流就可以 #include<cstdio> #include<cstring> #include<iostream> #

BZOJ 3993 Sdoi2015 星际战争 二分答案+最大流

题目大意:有n个机器人和m个激光武器,每个武器有一个威力和能打的集合,同一时刻只能打一个机器人,问最少多久可以全灭 二分答案+网络流= = 注意二分上界 #include <cstdio> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> #define M 110 #define S 0 #define T (M-1) #define E

【BZOJ】1189: [HNOI2007]紧急疏散evacuate(二分+bfs+网络流)

http://www.lydsy.com/JudgeOnline/problem.php?id=1189 表示完全不会QAQ.... 于是膜拜题解orz 二分时间........... 于是转换成判定性问题:即如何在有限时间内通过. 假设当前有t时间可供通过...那么每一个门最多能通过t个人........ 然后将所有能够到达门的点连边,容量为无限.... 然后源向每个点可行点连边..容量1.. 然后每个门向汇连边..容量为t.. 然后判断即可.... (一开始bfs写错了啊QAQ.. #inc

1189. [HNOI2007]紧急疏散EVACUATE【最大流+枚举或二分】

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