【bzoj1189】[HNOI2007]紧急疏散evacuate 二分+dinic

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

HINT

Source


不会建图……Fuckstorm神犇(Orz)教的我

二分时间ans,每次二分把距离门的距离不超过ans的点和门之间连一条流量为1的边。

源点向每个’.’连一条流量为1的边,门向汇点连一条流量为ans的边。

但是好像被hack了…在黄学长博客 -> 在这

4 5
XXDXX
XX.XX
X...X
XXDXX

其实也确实卡到我了…解决方法是把每个门拆成ans个点,每个点向他能连到的时间点连边(>=它到达门的最短时间)。

算法方面,网络流比较慢,所以改一下二分的上下界还是挺有必要的…数组memset也是,不要开太大…改完这俩我的程序快了近2000ms…

被hack的代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

const int SZ = 10010; 
const int INF = 1000000000; 

const int dx[] = {0,0,1,0,-1};
const int dy[] = {0,1,0,-1,0};

int head[SZ],nxt[SZ],tot = 1,s,e;

struct edge{
    int t,c;
}l[SZ];


inline void build(int f,int t,int c)
{
    l[++tot] = (edge) {t,c};
    nxt[tot] = head[f];
    head[f] = tot;
}

queue<int> q;

int deep[SZ];

inline bool bfs()
{
    memset(deep,0,sizeof(deep));
    deep[s] = 1;
    q.push(s);
    while(q.size())
    {
        int f = q.front(); q.pop();
        for(int i = head[f];i;i = nxt[i])
        {
            int v = l[i].t;
            if(l[i].c && !deep[v])
            {
                deep[v] = deep[f] + 1;
                q.push(v);
            }
        }
    }
    if(deep[e]) return true;
    return false;
}

bool vv[SZ];

inline int dfs(int u,int flow)
{
    if(u == e || flow == 0) return flow;
    int ans = 0;
    for(int i = head[u];i;i = nxt[i])
    if(!vv[i])
    {
        int v = l[i].t;
        if(l[i].c && deep[v] == deep[u] + 1)
        {
            vv[i] = 1;
            int f = dfs(v,min(flow , l[i].c));
            if(f > 0)
            {
                l[i].c -= f;
                l[i ^ 1].c += f;
                flow -= f;
                ans += f;
                vv[i] = 0;
            }
            if(flow == 0) break;
        }
    }
    if(ans == 0) deep[u] = INF;
    return ans;
}


inline int dinic()
{
    int ans = 0;
    while(bfs())
    {
        int tmp = dfs(s,INF);
        if(tmp == 0) break;
        ans += tmp;
        memset(vv,0,sizeof(vv));
    }
    return ans;
}





int n,m,sum = 0;

char maps[233][233];

inline int getnode(int x,int y)
{
    return (x - 1) * m + y;
}

struct xy{
    int x,y,step;
};

queue<xy> que;

bool vis[233][233];

inline void bfs(int x,int y,int ans)
{
    memset(vis,0,sizeof(vis));
    que.push((xy){x,y,0});
    while(que.size())
    {
        xy f = que.front(); que.pop();
        if(f.step == ans) continue;
        for(int i = 1;i <= 4;i ++)
        {
            int xx = f.x + dx[i];
            int yy = f.y + dy[i];
            if(xx > 0 && xx <= n && yy > 0 && yy <= m && maps[xx][yy] == '.' && !vis[xx][yy])
            {
                build(getnode(xx,yy),getnode(x,y),1); build(getnode(x,y),getnode(xx,yy),0); 
                vis[xx][yy] = 1;    
                que.push((xy){xx,yy,f.step + 1});
            }
        }
    }
}

inline void init()
{
    memset(head,0,sizeof(head));
    memset(nxt,0,sizeof(nxt));
    memset(l,0,sizeof(l));
    tot = 1;
}

inline void build_graph(int ans)
{
    init();
    s = n * m + 1; 
    e = n * m + 2; 
    for(int x = 1;x <= n;x ++)
    {
        for(int y = 1;y <= m;y ++)
        {
            if(maps[x][y] == 'D')
            {
                bfs(x,y,ans);
                build(getnode(x,y),e,ans);
                build(e,getnode(x,y),0);
            } 
            if(maps[x][y] == '.') build(s,getnode(x,y),1),build(getnode(x,y),s,0);
        }
    }
}

inline int div()
{
    int r = 400 , l = -1;
    while(r - l > 1)
    {
        int mid = (l + r) >> 1;
        build_graph(mid);
    //  cout << dinic() <<endl;
        if(dinic() >= sum) r = mid;
        else l = mid;
    }
    return r;
}


int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i ++)
    {
        scanf("%s",maps[i] + 1);
        for(int j = 1;j <= m;j ++)
            if(maps[i][j] == '.') sum ++; 
    }
    int ans = div();
    if(ans == 400) puts("impossible");
    else printf("%d",ans);
    return 0;
}

/*

20 3
XXX
X.X
X.D
X.X
X.X
X.X
D.X
X.X
D.X
X.X
X.X
X.X
X.X
X.X
X.X
X.X
X.X
X.X
X.X
XXX
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值