P3191 [HNOI2007] 紧急疏散EVACUATE 题解
题目描述
题目解法
看到数据范围:网络流
看到求时间:二分或分层
再看到给出的数据:BFS
所以我选择网络流+分层+BFS
建图
-
首先每个空地 \((i,j)\) 都有一个人,所以从源点 \(s\) 连一条流量为 \(1\) 的边到 \((i,j)\)。
-
然后对每个门 \(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\) 时刻的状态。
-
从 \(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&∑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;
}
}

浙公网安备 33010602011771号