W
e
l
c
o
m
e
: )

P3191 [HNOI2007] 紧急疏散EVACUATE 题解

题目描述

P3191 HNOI2007 紧急疏散EVACUATE

题目解法

看到数据范围:网络流

看到求时间:二分分层

再看到给出的数据:BFS

所以我选择网络流+分层+BFS

建图

  1. 首先每个空地 \((i,j)\) 都有一个人,所以从源点 \(s\) 连一条流量为 \(1\) 的边到 \((i,j)\)

  2. 然后对每个门 \(D_x\) 进行 BFS,若点 \((i,j)\)\(x\) 号门的距离为 \(d\),则说明从 \((i,j)\) 到门 \(D_x\) 最短耗时 \(d\)。由于每个门每个时刻只能撤离 \(1\) 个人,所以我们对门进行拆点,将门 \(D_x\) 拆成 \(t\) 个点,对于 \(i\in [1, t]\)\(D_{x,i}\) 就代表第 \(i\) 时刻的门。所以从 \((i,j)\) 连一条流量为 \(1\) 的边到 \(D_{x,d}\)\(D_{x,d}\) 代表第 \(x\) 号门的第 \(d\) 时刻的状态。

  3. \(D_{x,d}\) 连一条流量为 \(\infty\) 的边到 \(D_{x,d+1}\),该时刻没有撤离的人,可以向下一时刻 \(D_{x,d+1}\) 转移。

在 BFS 过程中判断是否有解。

本蒟蒻因为 BFS 写错 MLE on #2

分层

鉴于本题中 \(3\leq N\le20,3\le M\le20\),最多不超过 \(400\) 步即可撤离,使用分层,从 \(1\)\(400\) 依次枚举时刻 \(y\),每次在残量网络中从 \(D_{x,y}\) 连一条流量为 \(1\) 的边到汇点 \(t\)

在每次连边完成后在残量网络上跑最大流,如果最大流的和等于总人数,那么就输出 \(y\) 并退出。

因为每次跑最大流不需要重新建图,只需要连边,效率并不差。

Code

#include<bits/stdc++.h>
using namespace std;

template<typename Tp, size_t sizn, size_t sizm>
struct netflow
{
    int cnt=1, s=sizn-3, t=sizn-2;
    Tp val[sizm<<1], dis[sizn];

    void link(int u, int v, Tp w) 
    {
        to [++cnt]=v;       val [cnt]=w;
        nxt[ cnt ]=head[u]; head[ u ]=cnt;
        to [++cnt]=u;       val [cnt]=0;
        nxt[ cnt ]=head[v]; head[ v ]=cnt;
    }

    private:

    int head[sizn], to[sizm<<1], nxt[sizm<<1], now[sizm<<1];
    const Tp inf=((Tp)INFINITY)>>1;
    
    int bfs() 
    {
        for(int i=1;i<sizn;i++) dis[i]=inf;
        queue<int> q;
        q.push(s);
        dis[s]=0;
        now[s]=head[s];
        while (!q.empty())
        {
            int idx=q.front(); q.pop();
            for(int i=head[idx];i;i=nxt[i])
            {
                int arr=to[i];
                if(val[i]>0&&dis[arr]==inf)
                {
                    q.push(arr);
                    now[arr]=head[arr];
                    dis[arr]=dis [idx]+1;
                    if(arr==t) return 1;
                }
            }
        }
        return 0;
    }
    
    Tp dfs(int idx, Tp sum)
    {
        if(idx==t) return sum;
        Tp k, res=0;
        for(int i=now[idx];i&&sum;i=nxt[i])
        {
            now[idx]=i;
            int arr=to[i];
            if(val[i]>0&&(dis[arr]==dis[idx]+1))
            {
                k=dfs(arr, min(sum, val[i]));
                if(k==0) dis[arr]=inf;
                val[i]-=k;      res+=k;
                val[i^1]+=k;    sum-=k;
            }
        }
        return res;
    }
    public:
    
    Tp maxflow()
    {
        Tp ans=0;
        while (bfs()) ans+=dfs(s, inf); 
        return ans;
    }
};

netflow<int, 50004, 1000006> nf;

struct st
{
    int d, y, x;
    st(int x, int y, int d=0): x(x), y(y), d(d) {};
};

char mp[25][25];
int vis[25][25], dis[25][25];

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

int n, m;

#define chk(x, y) ((x)&&(y)&&((x)<=n)&&((y)<=m))
#define pos(x, y) (((x)-1)*m+(y))

queue<st> q;
int tot=0, cdd;
void bfs(int x, int y, int cc)
{
    q.emplace(x, y);
    memset(vis, 0, sizeof vis);
    while(!q.empty())
    {
        auto [d, y, x]=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int nx=x+dx[i];
            int ny=y+dy[i];
            if(!chk(nx, ny)||mp[nx][ny]!='.'||vis[nx][ny]) continue;
            dis[nx][ny]=vis[nx][ny]=1;
            nf.link(pos(nx, ny), n*m+cdd*d+cc, 1);
            q.emplace(nx, ny, d+1);
        }
    }
}

char gc()
{
    char c=getchar();
    while(c!='X'&&c!='.'&&c!='D') c=getchar();
    return c;
}

vector<pair<int, int> > vp;

int main()
{
    int cnt=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            mp[i][j]=gc();
            if(mp[i][j]=='D')
                vp.emplace_back(i, j), cdd++;
            if(mp[i][j]=='.')
                nf.link(nf.s, pos(i, j), 1), cnt++;
        }
    int tmp=0;
    for(auto [x, y]:vp) bfs(x, y, ++tmp);
    tmp=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            tmp+=dis[i][j];
    if(tmp!=cnt) return cout<<"impossible", 0;
    int flow=0;
    for(int i=1;;i++) 
    {
        for(int j=1;j<=cdd;j++)
            nf.link(n*m+(i-1)*cdd+j, nf.t, 1);
        for(int j=1;j<=cdd;j++) 
            nf.link(n*m+(i-1)*cdd+j, n*m+i*cdd+j, 0x3f3f3f3f);
        flow+=nf.maxflow();
        if(flow==cnt) return cout<<i, 0;
    }
}
posted @ 2024-08-25 20:58  Jimmy-LEEE  阅读(31)  评论(0)    收藏  举报