P3191 [HNOI2007] 紧急疏散EVACUATE 题解

题目描述

P3191 HNOI2007 紧急疏散EVACUATE

题目解法

看到数据范围:网络流

看到求时间:二分分层

再看到给出的数据:BFS

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

建图

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

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

  3. Dx,d 连一条流量为 的边到 Dx,d+1,该时刻没有撤离的人,可以向下一时刻 Dx,d+1 转移。

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

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

分层

鉴于本题中 3N20,3M20,最多不超过 400 步即可撤离,使用分层,从 1400 依次枚举时刻 y,每次在残量网络中从 Dx,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;
}
}

本文作者:Jimmy-LEEE

本文链接:https://www.cnblogs.com/redacted-area/p/18379532

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Jimmy-LEEE  阅读(6)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起