HDU 1733 escape(最大流 + dinic算法)

/*
超时,不过不想找错误了!做这道题开始接触dinic算法
如果想要学dinic算法,参见:
http://wenku.baidu.com/view/98deaf06b52acfc789ebc91a.html###
http://www.cnblogs.com/ltang/archive/2010/11/17/1879573.html

题意:
X代表人,@代表教室门,问所有的人都跑出教室需要多久,每次只能走一步而且所走位置不能有人。

题解:最大流 + dinic算法
具体构图如下:
A: 增加源点src,和汇点dest,然后根据每个时间点建出分层图,每个时间对应一层,对于每层图的构造如下
B:给每个格子标上号Xi, 由于每个格子一次只能占一人,所以把每个格子分为两个点xa,xb,连上容量为1的有向边,对于格子为‘X’的,(如果为第0层的话)在源点src与xa之间连一条容量为1的有向边,对于格子为'@'的点,在xb与汇点dest连上容量为1的有向边,对于每个格子,(除‘#’外),在xb与其上下左右及其本身 的对应下一层图的xa连上容量为1 的一条有向边
C:具体操作并不是一下子建出分层图,由于时间是未知的,所以枚举时间,做最大流,当最大流小于人数时,时间加一并在原图上增加一层,继续求最大流,直到最大流大于等于人数,这时的时间就是答案
*/

#include <iostream>
#define re(i, n) for(int i = 0; i < n; ++ i)
using namespace std;

const int Max = 20;
const int nMax = 320000;
const int mMax = 3200000;
const int INF = 0x7fffffff;
char map[Max][Max];
int mark[Max][Max];
int d[Max][Max];

int qx[nMax], qy[nMax], k;
struct Edge
{
	int v, w, next;
	Edge(){}
	Edge(int v, int w, int next): v(v), w(w), next(next){}
}adj[mMax];
int head[nMax], ind;
int dis[nMax];
int queue[nMax];

int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
int num, node, z;
int s, t;
int n, m;
int MaxFlow;

void buildDi(int _k)//这个函数主要对矩阵进行处理,能够到门口的所有节点都满足d[i][j] != 0,便于判断是否存在无论如何也出不去的同学
{
	memset(d, -1, sizeof(d));
	for(int i = 0; i < _k; ++ i)
		d[qx[i]][qy[i]] = 0;
	for(int i = 0; i < _k; ++ i)
	{
		for(int j = 0; j < 4; ++ j)
		{
			int x = qx[i] + dir[j][0];
			int y = qy[i] + dir[j][1];
			if(x >= 0 && x < n && y >= 0 && y < m && map[x][y] != '#' && d[x][y] == -1)
			{
				d[x][y] = d[qx[i]][qy[i]] + 1;
				qx[_k] = x;
				qy[_k] = y;
				_k ++;//对qx[],qy[]不断补充节点
			}
		}
	}
}

bool ok()//判断X与@是否可通
{
	re(i, n) re(j, m)
	{
		if(map[i][j] == 'X' && d[i][j] < 0) return 0;
	}
	return 1;
}

void addEdge(int u, int v, int w)
{
	adj[ind] = Edge(v, w, head[u]);
	head[u] = ind ++;
	adj[ind] = Edge(u, 0, head[v]);
	head[v] = ind ++;
}

void makeGraph()//每当时间增加1,则增加一层,建图参见上面详细介绍
{
	node += 2 * z;
	re(i, n) re(j, m)
		if(mark[i][j])
		{
			addEdge(node + mark[i][j], node + mark[i][j] + z, 1);
			addEdge(node + mark[i][j] - z, node + mark[i][j], 1);
			if(map[i][j] == '@') addEdge(node + mark[i][j] + z, t, 1);
			for(int k = 0; k < 4; ++ k)
			{
				int x = i + dir[k][0];
				int y = j + dir[k][1];
				if(x >= 0 && x < n && y >= 0 && y < m && mark[x][y])
				addEdge(node + mark[x][y] - z, node + mark[i][j], 1);
			}
		}
}

int dinicBfs()//dinic算法中建层次图的部分,同时判断是否存在可行弧
{
	memset(dis, -1, sizeof(dis));
	int rear, front;
	front = rear = 0;
	queue[rear ++] = s;
	dis[s] = 0;
	while(front < rear)
	{
		int u = queue[front ++];
		for(int id = head[u]; id != -1; id = adj[id].next)
		{
			int v = adj[id].v;
			if(adj[id].w && dis[v] == -1)
				//这里条件判断有疑问,因为这里建立层次图的目的是为了dinicDfs()可以查找到一条最短最广路,所以dis[v]之前应该没有被访问过
			{
				dis[v] = dis[u] + 1;
				if(v == t) return 1;
				queue[rear ++] = v;
			}
		}
	}
	return 0;
}

int dinicDfs(int cur, int cost = INF)//查找最短增广路径
{
	if(cur == t) return cost;
	int low;
	int ans = 0;
	for(int id = head[cur]; id != -1; id = adj[id].next)//这里其实并不只是寻找了一条增广路径,多条进行了汇合
	{
		int v = adj[id].v;
		if(adj[id].w && (dis[v] == dis[cur] + 1) && (low = dinicDfs(v, min(adj[id].w ,cost))))
		{
			adj[id].w -= low;
			adj[id ^ 1].w += low;
			ans += low;//各条增广路径最大流量之和
			cost -= low;//剩余可用流量
			if(!cost) break;
		}
	}
	return ans;
}

int dinicFlow()//dinic算法,输出最大流,因为题中特殊条件,所以MaxFlow需要定义成全局变量,并进行累加
{
	while(dinicBfs())
		MaxFlow += dinicDfs(s);
	return MaxFlow;
}

int main()
{
	//freopen("e://data.in", "r", stdin);
	while(scanf("%d %d", &n, &m) != EOF)
	{
		num = 0;
		k = 0;
		z = 0;
		re(i, n)
		{
			getchar();
			re(j, m)
			{
				map[i][j] = getchar();
				if(map[i][j] != '#') mark[i][j] = ++z;//每一层的节点数
				else mark[i][j] = 0;
				if(map[i][j] == '@') qx[k] = i, qy[k] = j, ++k;//存储教室门的坐标
				if(map[i][j] == 'X') ++num;//总人数
			}
		}
		buildDis(k);
		if(!ok())
		{
			printf("-1\n");
			continue;
		}
		s = 0;
		t = 1;
		node = 1;
		ind = 0;
		memset(head, -1, sizeof(head));
		re(i, n) re(j, m)
		{
			if(mark[i][j])
			{
				if(map[i][j] == 'X') addEdge(s, mark[i][j] + node, 1);
				addEdge(mark[i][j] + node, mark[i][j] + node + z, 1);
			}
		}
		int ans = 0;
		MaxFlow = 0;
		while(dinicFlow() < num) 
		{
			makeGraph();
			ans++;
		}
		printf("%d\n", ans);
	}
	return 0;
}

你可能感兴趣的:(HDU 1733 escape(最大流 + dinic算法))