HNOI2007.BZOJ1189.紧急疏散(最大流 && 二分)

有一个 N * M 的矩形区域,每个格子如果是 ‘ . ’ 则此处有一个人,如果是 ‘ D ’ 则这里是门(门只出现在边界上),否则这里就是墙。每个人可以在单位 1 的时间走到与其相邻的上下左右任意一个格子里,也可以不动。每个格子可以站无数个人,但是每扇门每一个单位时间只能出去一个人,问所有人离开这个矩形区域所需最短时间,如果不行,输出 ‘ impossible ' 。

二分时间,再用网络流判断:S 向每个人连一条 1 的边,每个人向在二分所得时间内能到达的门连 1 的边,每扇门向 T 连容量为二分的时间的边。看最后的最大流是不是等于总人数。看起来很正确,但是我们忽略了题目中的一个条件,即 “每扇门每一个单位时间只能出去一个人”。比如这种情况:

XXDX

X . . X

X . . X

XXXX

正确答案是 4,而我们所求的答案是 3 ,因为最后两个人是同时从门里出来的。怎么办呢?我们把每个门按时间拆点:S 向每个人连一条 1 的边,每个人向二分时间能到达的时间连边 1,即设二分时间为 X,第 i 个人到达 j 号门的时间为 Y, 且 Y <= X,既然 iY时间就能到门口,那么 i 也没有必要立刻就出去,在 Y ~ X 这段时间他都可以出去,所以缓一缓,就能避免同时出去的情况,最后每个时间的门向 T1 的边,最大流判断即可。

#include 
#include 
#include 

using namespace std;

const int MAX_N = 25000, MAX_M = 2005000;
const int inf = 1000000007;

struct node {
	int v, w, next;
}E[MAX_M << 1];
int head[MAX_N], cur[MAX_N], top = 0;
int n, m, tot = 0, S, T, rx[405], ry[405], fx[405], fy[405], dor = 0, mp[25][25];
int dep[MAX_N], q[MAX_M], dis[405][25][25], s[MAX_M], xx[MAX_M], yy[MAX_M];
char str[25];
int dx[4] = {-1, 0, 0, 1}, dy[4] = {0, -1, 1, 0};

inline void add(int u, int v, int w, int x) {
	int t1 = x << 1, t2 = t1 + 1;
	E[t1].v = v; E[t1].w = w; E[t1].next = head[u]; head[u] = t1;
	E[t2].v = u; E[t2].w = 0; E[t2].next = head[v]; head[v] = t2;
}
inline bool bfs1(int k, int x, int y) {
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j <= m; j ++) dis[k][i][j] = inf;
	int bg = 0, ed = 1;
	s[bg] = 0; xx[bg] = x; yy[bg] = y;
	while (bg < ed) {
		for (int i = 0; i < 4; i ++) {
			int nx = xx[bg] + dx[i], ny = yy[bg] + dy[i];
			if (nx < 1 || nx > n || ny < 1 || ny > m || !mp[nx][ny]) continue;
			if (dis[k][nx][ny] == inf) {
				dis[k][nx][ny] = s[bg] + 1;
				if (mp[nx][ny] == 2) continue;
				s[ed] = s[bg] + 1;
				xx[ed] = nx; yy[ed] = ny; ed ++;  
			}
		}
		bg ++;
	} 
}
inline bool bfs() {
	memset(dep, -1, sizeof(dep));
	int bg = 0, ed = 1;
	q[ed] = S; dep[S] = 0;
	while (bg < ed) {
		int x = q[++ bg];
		for (int i = head[x]; i != -1; i = E[i].next) {
			if (dep[E[i].v] == -1 && E[i].w) {
				dep[E[i].v] = dep[x] + 1; q[++ ed] = E[i].v;
				if (E[i].v == T) return 1;
			}
		}
	}
	return 0;
}
int dfs(int x, int mx) {
	if (x == T) return mx;
	int ret;
	for (int &i = cur[x]; i != -1; i = E[i].next) 
		if (E[i].w && dep[E[i].v] == dep[x] + 1 && (ret = dfs(E[i].v, min(mx, E[i].w)))) {
			E[i].w -= ret; E[i ^ 1].w += ret;
			return ret;
		}
	return 0;
}
inline int dinic() {
	int ans = 0, ret;
	while (bfs()) {
		for (int i = 0; i <= T; i ++) cur[i] = head[i];
		while (ret = dfs(S, inf)) ans += ret;
	}
	return ans;
}
inline bool check(int x) {
	S = 0; 
	memset(head, -1, sizeof(head)); top = 0;
	for (int i = 1; i <= tot; i ++) add(S, i, 1, top ++);
	for (int i = 1; i <= tot; i ++) 
		for (int j = 1; j <= dor; j ++)	
			if (dis[i][fx[j]][fy[j]] <= x){
				for (int k = dis[i][fx[j]][fy[j]]; k <= x; k ++){ 
					int k1 = i, k2 = k * dor - dor + j + tot;
					add(k1, k2, 1, top ++);
				}	
			}
	T = tot + x * dor + 1;
	for (int i = 1; i <= x * dor; i ++) add(tot + i, T, 1, top ++);
	if (dinic() == tot) return 1;
	return 0;
}
void init() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i ++) {
		scanf("%s", str + 1);
		for (int j = 1; j <= m; j ++) {
			if (str[j] == '.') rx[++ tot] = i, ry[tot] = j, mp[i][j] = 1;
			else if (str[j] == 'D') fx[++ dor] = i, fy[dor] = j, mp[i][j] = 2;
			else mp[i][j] = 0;
		}
	}
	for (int i = 1; i <= tot; i ++)
		bfs1(i, rx[i], ry[i]);
}
void doit() {
	int l = 0, r = 400, mid;
	while (l <= r) {
		mid = (l + r) >> 1; 
		if (check(mid)) r = mid - 1;
		else l = mid + 1;
	}
	if (l >= 400) printf("impossible\n");
	else printf("%d\n", l);
}
int main() {
	init();
	doit();
	return 0;
} 


  

你可能感兴趣的:(HNOI2007.BZOJ1189.紧急疏散(最大流 && 二分))