HDU3879:Base Station

——建通讯站。每个通讯站有一定耗费,两个特定的通讯站之间建立通讯以后会有一定收益,问怎样建立通讯站可以使得收益最大

——最大权闭合子图-》最小割

——url:http://acm.hdu.edu.cn/showproblem.php?pid=3879

——————————————————————————————————————————

首先考虑将图转化。

即一条通讯线路有一定收益,但需要建立两个通讯站,这两个通讯站有一定造价。

将通讯线路也变成点,点权为收益,连两条有向只向两个通讯站。

原来的通讯站点权不变。

由此,题目转化为在新的图中求一个闭合图,使得其点权最大,即最大权闭合子图

(闭合图个人理解的定义:对于点集中的所有点,其后继结点一定在该点集中)

——————————————————————————————————————————————

最大权闭合子图的求解参见IOI2007胡伯涛的论文。

新建一个源点S,S与每个通讯线路的点相连,边权为收益

新建一个汇点T,T与每个通讯站相连,边权为建造费用

通讯站与通讯线路之间的边权为无穷大

最后的结果为所有通讯线路总的收益-最小割

#include<stdio.h>
#include<memory.h>
#define N 56000
#define oo 0x7ffffff
int head[N], nxt[N * 10], ev[N*10],ew[N*10];
int pi[N], pe[N];
int CurrentNode[N];
int queue[N];
int d[N];
int numbs[N];
int n, m, node_sum, source, sink, ans, num;
void addedge(int u, int v, int w)
{
	nxt[++num] = head[u];
	head[u] = num;
	ev[num] = v;
	ew[num] = w;
	nxt[++num] = head[v];
	head[v] = num;
	ev[num] = u;
	ew[num] = 0;
}
void readin()
{
	int i, a, b, c;
	num = -1;
	memset(head, -1, sizeof(head));
	memset(nxt, -1, sizeof(nxt));
	for (i = 1; i <= n; i++)
	{
		scanf("%d", &c);
		addedge(i, n + m + 2, c);
	}
	node_sum = n;
	ans = 0;
	for (i = 0; i < m; i++)
	{
		node_sum++;
		scanf("%d%d%d", &a, &b, &c);
		addedge(node_sum, a, oo);
		addedge(node_sum, b, oo);
		addedge(n + m + 1, node_sum, c);
		ans += c;
	}
	source = n + m + 1;
	sink = n + m + 2;
	node_sum = n + m + 2;
}
int Augment(int source, int sink)
{
	int i, j, tmp, width(oo);
	for (i = sink, j = pi[i]; i != source; i = j, j = pi[j])
	{
		tmp = ew[pe[i]];
		if (tmp < width)
			width = tmp;
	}
	for (i = sink, j = pi[i]; i != source; i = j, j = pi[j])
	{
		ew[pe[i]] -= width;
		ew[pe[i] ^ 1] += width;

	}
	return width;
}
int Retreat(int &i, int n, int source, int sink)
{
	int tmp;
	int j, mind(n - 1);
	for (j = head[i]; j != -1; j = nxt[j])
		if (ew[j] > 0 && d[ev[j]] < mind)
			mind = d[ev[j]];
	tmp = d[i];
	numbs[d[i]]--;
	d[i] = 1 + mind;
	numbs[d[i]]++;
	if (i != source)
		i = pi[i];
	return numbs[tmp];
}
int find_max_flow(int n, int source, int sink)
{
	int flow(0), i, j;
	memset(d, 0, sizeof(d));
	memset(numbs, 0, sizeof(numbs));
	numbs[0] = n;
	for (i = 1; i <= n; i++)
		CurrentNode[i] = head[i];
	i = source;
	for (; d[source] < n;)
	{
		for (j = CurrentNode[i]; j != -1; j = nxt[j])
			if (ew[j] > 0 && d[i] == d[ev[j]] + 1)
				break;
		if (j != -1)
		{
			CurrentNode[i] = j;
			pi[ev[j]] = i;
			pe[ev[j]] = j;
			i = ev[j];
			if (i == sink)
			{
				flow += Augment(source, sink);
				i = source;
			}
		}
		else
		{
			CurrentNode[i] = head[i];
			if (Retreat(i, n, source, sink) == 0)
				break;
		}
	}
	return flow;
}
int main()
{
	while (scanf("%d%d", &n, &m) != EOF)
	{
		readin();
		ans -= find_max_flow(node_sum, source, sink);
		printf("%d\n", ans);
	}
	return 0;
}

  

posted on 2011-08-07 18:52  风也轻云也淡  阅读(433)  评论(0编辑  收藏  举报