BZOJ 1189 HNOI 2007 紧急疏散 evacuate 二分答案 最大流

题目大意:紧急疏散。有一张地图,‘.’表示人,‘D’表示门,人需要走曼哈顿距离的单位时间才1能到达门。一个门一个时刻只能通过一个人。求多长时间能疏散完毕。

思路:二分答案+最大流满流判定。先BFS处理出每个人与门的距离。二分最小时间,然后连边。S向每个人连流量为1的边,每个人向二分的时间之内能到达的门连流量为1的边。每个门向T连流量为t的边。然后最大流判定是否满流。

(数组大小我是瞎开的,写代码的时候要算好了在开!)

CODE:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 10010
#define S 0
#define T 5000
#define INF 0x7f7f7f7f
using namespace std;
const int dx[] = {0,0,0,1,-1};
const int dy[] = {0,1,-1,0,0};

struct Door{
	int x,y;
	Door(int _,int __):x(_),y(__) {}
	Door() {}
}door[MAX];
struct Empty{
	int x,y;
	Empty(int _,int __):x(_),y(__) {}
	Empty() {}
}person[MAX];
struct Status{
	int x,y;
	int length;

	Status(int _,int __,int ___)
		:x(_),y(__),length(___) {}
	Status() {}
};

int m,n;
int doors,persons;
char src[30][30];
int num[30][30],dis[MAX][410];
bool v[30][30];

int head[MAX],total;
int _next[2000000],aim[2000000],flow[2000000];

int deep[MAX];

inline void BFS(int p);
inline void MakeGraph(int t);
inline void Initialize();
inline void Add(int x,int y,int f);

bool BFS();
int Dinic(int x,int f);

int main()
{
	cin >> m >> n;
	for(int i = 1;i <= m; ++i) {
		scanf("%s",src[i] + 1);
		for(int j = 1;j <= n; ++j) {
			if(src[i][j] == 'D') {
				door[++doors] = Door(i,j);
				num[i][j] = doors;
			}
			else if(src[i][j] == '.') {
				person[++persons] = Empty(i,j);
				num[i][j] = persons;
			}
		}
	}
    memset(dis,0x3f,sizeof(dis));
	for(int i = 1;i <= doors; ++i)
		BFS(i);
	int l = 0,r = 400,ans = -1;
	while(l <= r) {
		int mid = (l + r) >> 1;
		MakeGraph(mid);
		int max_flow = 0;
		while(BFS())
			max_flow += Dinic(S,INF);
		if(max_flow == persons)
			ans = mid,r = mid - 1;
		else	l = mid + 1;
	}
	if(ans == -1)	puts("impossible");
	else	cout << ans << endl;
	return 0;
}

inline void BFS(int p)
{
	static queue<Status> q;
    while(!q.empty())   q.pop();
	memset(v,false,sizeof(v));
	q.push(Status(door[p].x,door[p].y,0));
	while(!q.empty()) {
		Status now = q.front(); q.pop();
		v[now.x][now.y] = true;
		for(int i = 1;i <= 4; ++i) {
			int fx = now.x + dx[i];
			int fy = now.y + dy[i];
			if(v[fx][fy] || !fx || !fy || fx > m || fy > n)	continue;
            if(src[fx][fy] == '.') {
                dis[num[fx][fy]][p] = now.length + 1;
                q.push(Status(fx,fy,now.length + 1));
            }
		}
	}
}

inline void MakeGraph(int t)
{
	Initialize();
	for(int i = 1;i <= persons; ++i)
		Add(S,i,1),Add(i,S,0);
	for(int i = persons + 1;i <= persons + doors; ++i)
		Add(i,T,t),Add(T,i,0);
	for(int i = 1;i <= persons; ++i)
		for(int j = 1;j <= doors; ++j)
			if(dis[i][j] <= t)
				Add(i,j + persons,1),Add(j + persons,i,0);
}

inline void Initialize()
{
	total = 1;
	memset(head,0,sizeof(head));
}

inline void Add(int x,int y,int f)
{
	_next[++total] = head[x];
	aim[total] = y;
	flow[total] = f;
    head[x] = total;
}

bool BFS()
{
	static queue<int> q;
    while(!q.empty())   q.pop();
	memset(deep,0,sizeof(deep));
	deep[S] = 1;
	q.push(S);
	while(!q.empty()) {
		int x = q.front(); q.pop();
		for(int i = head[x];i;i = _next[i])
			if(flow[i] && !deep[aim[i]]) {
				deep[aim[i]] = deep[x] + 1;
				q.push(aim[i]);
				if(aim[i] == T)	return true;
			}
	}
	return false;
}

int Dinic(int x,int f)
{
	if(x == T)	return f;
	int temp = f;
	for(int i = head[x];i;i = _next[i])
		if(flow[i] && temp && deep[aim[i]] == deep[x] + 1) {
			int away = Dinic(aim[i],min(flow[i],temp));
			flow[i] -= away;
			flow[i^1] += away;
			temp -= away;
		}
    if(temp == f)   deep[x] = 0;
	return f - temp;
}
时间: 2024-10-04 04:16:54

BZOJ 1189 HNOI 2007 紧急疏散 evacuate 二分答案 最大流的相关文章

BZOJ 1189 HNOI2007 紧急疏散evacuate 二分答案+最大流

题目大意:给定一个m*n的地图,每个点有可能是空地.墙或者出口,每个空地初始站着一个人,每一时刻可以向周围走1格,门每一时刻只能通过一个人,求最短多少时间后所有人可以撤离 首先从每个出口出发开始广搜,得到每个空地到所有出口的距离 然后二分答案,每次建图如下: 从源点向每个空地一条流量为1的边 如果一个空地能在规定时间到达某个出口,就从这个空地出发向该出口链接一条流量为1的边 每个出口向汇点连接一条流量为时间的边 然后跑最大流验证即可 注意图有不连通的情况 所以广搜要清初值(这个没人会忘吧QAQ

【BZOJ1189】【HNOI2007】紧急疏散evacuate 二分答案+最大流check

#include <stdio.h> int main() { puts("转载请注明出处"); puts("地址:blog.csdn.net/vmurder/article/details/43666807"); } 题解: 首先floyd或者bfs求出每个'.'到各个'D'的最短路 然后注意一个点不能经过一个门去另一个门,所以可以看到我的floyd略有一点点点点不同... 然后这个时间限制可以转化为对每个门的拆点,可以证明拆400个就够了. 然后分别

【BZOJ 1189】[HNOI2007]紧急疏散evacuate

Description 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间.已知门一定在房间的边界上,并且边界上不会有空地.最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动.疏散开始后,每块空地上就没有人数限制了(也就是说每块空地可以同时站无数个人).但是,由于门很窄,每一秒钟只能有

POJ 2112 Optimal Milking 二分答案+最大流

首先二分最长的边,然后删去所有比当前枚举的值长的边,算最大流,看是否能满足所有的牛都能找到挤奶的地方 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include

BZOJ1189: [HNOI2007]紧急疏散evacuate 二分+最大流

1189: [HNOI2007]紧急疏散evacuate Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1132  Solved: 412[Submit][Status][Discuss] Description 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间.已知门一定在房间的边界上,并且

BZOJ 1305 CQOI2009 dance跳舞 二分答案+最大流

题目大意:给定n个男生和n个女生,一些互相喜欢而一些不.举行几次舞会,每次舞会要配成n对.不能有同样的组合出现.每一个人仅仅能与不喜欢的人跳k次舞,求最多举行几次舞会 将一个人拆成两个点.点1向点2连一条流量为k的边.两个人若互相喜欢则点1之间连边,不喜欢则点2之间连边 对于每个要验证的x值 将每个人的点1向源或汇连一条流量为x的边 然后二分答案跑最大流就可以 #include<cstdio> #include<cstring> #include<iostream> #

BZOJ 1738: [Usaco2005 mar]Ombrophobic Bovines 发抖的牛( floyd + 二分答案 + 最大流 )

一道水题WA了这么多次真是.... 统考终于完 ( 挂 ) 了...可以好好写题了... 先floyd跑出各个点的最短路 , 然后二分答案 m , 再建图. 每个 farm 拆成一个 cow 点和一个 shelter 点, 然后对于每个 farm x : S -> cow( x ) = cow( x ) 数量 , shelter( x ) -> T = shelter( x ) 容量 ; 对于每个dist( u , v ) <= m 的 cow( u ) -> shelter( v

BZOJ 3007 解救小云公主 二分答案+对偶图

题目大意:给定一个矩形和矩形内的一些点.求一条左下角到右上角的路径.使全部点到这条路径的最小距离最大 最小距离最大.果断二分答案 如今问题转化成了给定矩形中的一些圆形障碍物求左下角和右上角是否连通 然后就是对偶图的问题了 左下角和右上角连通等价于对偶图中左上两条边和右下两条边不连通 因此将全部相交的圆之间连边,从左上两条边广搜就可以 时间复杂度O(n^2log(min(r,l)/EPS)) #include <cmath> #include <cstdio> #include &l

BZOJ 2440: [中山市选2011]完全平方数 二分答案 + 容斥原理 + 莫比乌斯反演

http://www.lydsy.com/JudgeOnline/problem.php?id=2440 第一道莫比乌斯反演的题目. 二分答案 + 容斥那里还是挺好想的. 二分一个答案val,需要[1, val]之间存在的合法数字个数 >= k即可. 怎么判断呢?可以容斥,开始的时候有ans = val个,但是这里明显有些数字不符合. ans -= ([1...val]中有多少个2^2倍  + [1...val]中有多少个3^2倍 + [1...val]中有多少个5^2倍) ...... 但是减