【最大流+floyd+二分+dinic】北大 poj 2112 Optimal Milking


/* THE PROGRAM IS MADE BY PYY */
/*----------------------------------------------------------------------------//
    Copyright (c) 2012 panyanyany All rights reserved.

    URL   : http://poj.org/problem?id=2112
    Name  : 2112 Optimal Milking

    Date  : Friday, February 10, 2012
    Time Stage : 4 hours

    Result:                                                                                  9791440	panyanyany
2112
Accepted	592K	172MS	C++
4274B	2012-02-10 15:28:44

Test Data :
2 3 2
0 3 2 1 1
3 0 3 2 0
2 3 0 1 0
1 2 1 0 2
1 0 0 2 0

1 1 1
0 1
1 0
  
2 2 1
0 0 1 3
0 0 2 100
1 2 0 0
3 100 0 0

Review :
犯了一些概念性的错误,太失败了……
//----------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>


#define MEM(a, v)		memset (a, v, sizeof (a))	// a for address, v for value
#define max(x, y)		((x) > (y) ? (x) : (y))
#define min(x, y)		((x) < (y) ? (x) : (y))

#define INF		(0x3f3f3f3f)
#define MAXN	(234)

#define DB	/##/

int		K, C, M, n, low, hig ;
int		map[MAXN][MAXN], net[MAXN][MAXN], level[MAXN], q[MAXN] ;

void floyd()
{
	int i, j, k ;

	for (k = 1 ; k <= n ; ++k)
		for (i = 1 ; i <= n ; ++i)
			for (j = i+1 ; j <= n ; ++j)
			{
				map[j][i] = map[i][j] = min(map[i][j], map[i][k]+map[k][j]) ;
			}
}

int dinic (const int beg, const int end)
{
	int sum, i, u, v, head, tail ;
	sum = 0 ;

	while (true)
	{
		head = tail = 0 ;
		q[tail++] = beg ;
		MEM (level, -1) ;
		level[beg] = 0 ;

		while (head < tail)
		{
			u = q[head++] ;
			for (i = beg ; i <= end ; ++i)
				if (net[u][i] > 0 && level[i] == -1)
				{
					level[i] = level[u] + 1 ;
					if (end == i)
					{
						head = tail ;
						break ;
					}
					q[tail++] = i ;
				}
		}
		
		if (-1 == level[end])
			break ;

		tail = 0 ;
		q[tail++] = beg ;
		u = beg ;
		while (1)
		{
			if (end == u)
			{
				int flow = INF, qbreak ;
				for (i = 1 ; i < tail ; ++i)
				{
					u = q[i-1] ;
					v = q[i] ;
					if (flow >= net[u][v])
					{
						flow = net[u][v] ;
						qbreak = i - 1 ;
					}
				}
				sum += flow ;
				for (i = 1 ; i < tail ; ++i)
				{
					u = q[i-1] ;
					v = q[i] ;
					net[u][v] -= flow ;
					net[v][u] += flow ;
				}

				u = q[qbreak] ;
				tail = qbreak + 1 ;
			}

			for (i = beg ; i <= end ; ++i)
				if (net[u][i] > 0 && level[u]+1 == level[i])
					break ;

			if (i > end)
			{
				if (tail-1 == 0)
					break ;
				level[q[--tail]] = -1 ;
				u =q[tail-1] ;
			}
			else
			{
				q[tail++] = i ;
				u = i ;
			}
		}
	}
	return sum ;
}

void makegraph(const int lim)
{
	int i, j ;
	MEM(net, 0) ;
	for (i = 1 ; i <= K ; ++i)
		// 错误代码:for (j = 1 ; j <= n ; ++j)
		// 错误代码: for (j = i+1 ; j <= n ; ++j)
		// 首先,流向必须是单向的,map[u][v] 若有流量,则map[v][u]的流量须为0
		// 其次,必须是由挤奶机流向奶牛,或者从奶牛流向挤奶机。
		for (j = K+1 ; j <= n ; ++j)
			net[i][j] = (map[i][j] <= lim) ;//? INF : 0 ;

	// 源点到所有挤奶机的流量为 M
	for (i = 1 ; i <= K ; ++i)
		net[0][i] = M ;
	// 所有奶牛到汇点的流量为 1
	for (i = K+1 ; i <= n ; ++i)
		net[i][n+1] = 1 ;
}

int main()
{
	int i, j ;
	int ans, tmpans ;

	while (scanf ("%d%d%d", &K, &C, &M) != EOF)
	{
		MEM (net, 0) ;
		MEM (map, 0) ;
		n = K+C ;
		for (i = 1 ; i <= n ; ++i)
			for (j = 1 ; j <= n ; ++j)
			{
				scanf("%d", &map[i][j]) ;
				if (0 == map[i][j])
					map[i][j] = INF ;
			}
		// 求任意两点间最短路,为二分高度做准备
		floyd() ;

		for (i = 1 ; i <= K ; ++i)
			net[0][i] = M ;
		for (i = K+1 ; i <= n ; ++i)
			net[i][n+1] = 1 ;

		ans = INF ;	// 保险起见,先初始化一下
		int mid ;
		// 本来是在 floyd 里面对每条路径进行比较的,然后可以缩小[low,hig]的区间
		// 但是这样代码量大了,比较不容易维护,所以在后来查错的时间就挪出来,
		// 直接赋值了
		low = 0 ;
		hig = INF ;
		while (low <= hig)
		{
			mid = (low + hig) / 2 ;
			// 每次限定改变了,就要重新制一次图
			// 本来是可以在 dinic 里面增加一些代码,在制层次图和增广路的时候对
			// 路径检查距离,从而限定它们的距离的。比如:
			// if (map[u][i] <= mid && net[u][i] > 0 && level[i] == -1)
			// 但是这样一来,相当于给本来完好的代码埋下了未知的地雷。
			// 如果哪里漏了对 map[u][i]<=mid 的判断的话,就很难查错了
			makegraph(mid) ;

			tmpans = dinic (0, n+1) ;
			if (tmpans == C)
			{
				ans = mid ;		// 一开始会习惯性地写成 ans = tmpans 。
				hig = mid - 1 ;
			}
			else
				low = mid + 1 ;
		}
		printf ("%d\n", ans) ;
	}
	return 0 ;
}


你可能感兴趣的:(【最大流+floyd+二分+dinic】北大 poj 2112 Optimal Milking)