[HNOI2007]紧急疏散 EVACUATE
题目链接
数据范围非常小,一共20*20个点,首先考虑网络流。
题中的限制条件就是每个出口每一时刻限通过1人,换言之,一共撤离时间为t的话,这个出口最多通过t人。
求时间的最小值,那么可以考虑二分这个时间t。
那就可以考虑连边了,显然这个t人就是一个流量限制。建立超级源点S、T,S向每个"."连流量为1的边,每个出口向T连流量为t的边。每个"."向它距离不超过t的出口连流量为1的边。
只要最大流等于总人数就说明这是个合法解。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<vector>
#include<set>
#include<map>
#include<string>
#include<iostream>
#include<queue>
#include<cctype>
using namespace std;
#define A(x) cout << #x << " " << x << endl;
#define AA(x,y) cout << #x << " " << x << #y << " " << y << endl;
#define B cout << "Break" << endl;
#define ll long long
int read()
{
char c = getchar();
int x = 0,f = 1;
while(!isdigit(c))
{
if(c == '-') f = -1;
c = getchar();
}
while(isdigit(c))
{
x = x * 10 + c - '0';
c = getchar();
}
return f * x;
}
#define inf 1000000000
#define N 100010
#define M 500010
int head[N],nxt[M],to[M],val[M];
int ecnt = 1,tot,a,b;
void Add(int u,int v,int w)
{
nxt[++ecnt] = head[u];
head[u] = ecnt;
to[ecnt] = v;
val[ecnt] = w;
}
void add(int u,int v,int w)
{
Add(u,v,w);
Add(v,u,0);
}
int s,t;
int cur[N],dep[N];
bool bfs()
{
queue<int>q;
while(!q.empty()) q.pop();
memset(dep,-1,sizeof(dep));
dep[s] = 0;
q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
for(int i = head[u];i;i = nxt[i])
{
int v = to[i];
if(dep[v] == -1 && val[i] > 0)
{
dep[v] = dep[u] + 1;
q.push(v);
}
}
}
return dep[t] != -1;
}
int dfs(int u,int flow)
{
if(u == t) return flow;
int used = 0,tmp = 0;
for(int &i = cur[u];i;i = nxt[i])
{
int v = to[i];
if(dep[v] == dep[u] + 1 && val[i] > 0)
{
tmp = dfs(v,min(val[i],flow - used));
if(tmp > 0)
{
val[i] -= tmp;
used += tmp;
val[i ^ 1] += tmp;
if(used == flow) return used;
}
}
}
if(used != flow) dep[u] = -1;
return used;
}
int maxflow()
{
int tmp,ans = 0;
while(bfs())
{
memcpy(cur,head,sizeof(head));
while((tmp = dfs(s,inf))) ans += tmp;
}
return ans;
}
void reset()
{
memset(head,0,sizeof(head));
memset(nxt,0,sizeof(nxt));
memset(to,0,sizeof(to));
memset(val,0,sizeof(val));
ecnt = 1;
}
char mp[30][30];
int xx[5] = {0,0,1,-1};
int yy[5] = {1,-1,0,0};
struct node
{
int x,y;
};
vector<node>pot,door;
int get(int x,int y)
{
return b * (x - 1) + y;
}
int dis[1010][30][30];
void Run()
{
memset(dis,-1,sizeof(dis));
queue<node>q;
for(int i = 0;i < door.size();i++)
{
int sx = door[i].x,sy = door[i].y;
dis[i][sx][sy] = 0;
while(!q.empty()) q.pop();
q.push((node){sx,sy});
while(!q.empty())
{
int x = q.front().x,y = q.front().y;
q.pop();
for(int j = 0;j < 4;j++)
{
int nx = x + xx[j],ny = y + yy[j];
if(nx <= 0 || nx > a || ny <= 0 || ny > b || mp[nx][ny] == 'X' || dis[i][nx][ny] != -1) continue;
dis[i][nx][ny] = dis[i][x][y] + 1;
q.push((node){nx,ny});
}
}
}
return;
}
bool check(int tim)
{
reset();
for(int i = 0;i < pot.size();i++)
{
int x = pot[i].x,y = pot[i].y;
add(s,get(x,y),1);
}
for(int i = 0;i < door.size();i++)
{
int x = door[i].x,y = door[i].y;
add(get(x,y),t,tim);
}
for(int i = 0;i < door.size();i++)
{
for(int j = 0;j < pot.size();j++)
{
int x = pot[j].x,y = pot[j].y;
if(dis[i][x][y] <= tim && dis[i][x][y] != -1) add(get(x,y),get(door[i].x,door[i].y),1);
}
}
return maxflow() >= (int)pot.size();
}
int main()
{
memset(mp,0,sizeof(mp));
scanf("%d %d",&a,&b);
for(int i = 1;i <= a;i++) scanf("%s",mp[i] + 1);
pot.clear(),door.clear();
for(int i = 1;i <= a;i++)
{
for(int j = 1;j <= b;j++)
{
if(mp[i][j] == '.')
pot.push_back((node){i,j});
else if(mp[i][j] == 'D')
door.push_back((node){i,j});
}
}
s = 10001,t = 10002;
int l = 0,r = a * b + 1;
Run();
while(l < r)
{
int mid = (l + r) >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
if(l <= a * b) printf("%d\n",l);
else puts("impossible");
}