【最大闭合权子图】[noi2006] codevs1789 最大获利

题目点这里 每日一道练代码的题!233不过orz最开始空间开跪了


题意:每条边有个边权 每个点有个点权 求边权-点权最大的一个图


把边变成一个点 然后指向两边端点 于是转化成最大闭合权子图 于是求总边权-最小割就行了

关于最大闭合权子图和最小割的转化(来源:http://blog.sina.com.cn/s/blog_48f85e1d0100mxem.html):

记一个简单割的容量为C,且S所在集合为N,T所在集合为M。
        则C=M中所有权值为正的点的权值(即S与M中点相连的边的容量)+N中所有权值为负的点权值的绝对值(即N中点与T中点相连边的容量),记(C=x1+y1)。
        记N这个闭合图的权值和为W。
        则W=N中权值为正的点的权值-N中权值为负的点的权值的绝对值。记(W=x2-y2);
        则W+C=x1+y1+x2-y2。
        因为y1=y2,所以W+C=x1+x2
        x1为M中所有权值为正的点的权值,x2为N中权值为正的点的权值。
        所以x1+x2=所有权值为正的点的权值之和(记为TOT).
        所以我们得到W+C=TOT.整理一下W=TOT-C.
        到这里我们就得到了闭合图的权值与简单割的容量的关系。
        因为TOT为定值,所以我们欲使W最大,即C最小,即此时这个简单割为最小割,此时闭合图为其源点S所在集合(除去S)。得证。


#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

int read()
{
	int sign = 1, n = 0; char c = getchar();
	while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }
	while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }
	return sign*n;
}

const int inf = 0x3f3f3f3f;
const int Nmax = 55005;
const int Mmax = 50005;


int N, M; 
int S, T;
int tot;

struct ed{
	int v, flow, next;
}e[Mmax * 4];
int k = 2, head[Nmax], cur[Nmax];

inline void adde(int u, int v, int f)
{
	e[k] = (ed){ v, f, head[u] };
	head[u] = k++;
	e[k] = (ed){ u, 0, head[v] };
	head[v] = k++;
}

int dis[Nmax];
queue <int> q;

inline bool bfs()
{
	memcpy(cur, head, sizeof(cur));
	memset(dis, -1, sizeof(dis));
	while(q.size()) q.pop(); q.push(S); dis[S] = 0;
	
	while(q.size())
	{
		int u = q.front(); q.pop();
		for(int i = head[u]; i; i = e[i].next)
		{
			int v = e[i].v;
			if(e[i].flow && dis[v] == -1)
			{
				dis[v] = dis[u] + 1;
				if(v == T) return 1;
				q.push(v);
			} 
		}
	}
	return 0;
}

int dfs(int u, int maxf)
{
	if(u == T || !maxf) return maxf;
	
	int flow = 0, f;
	for(int &i = cur[u]; i; i = e[i].next)
	{
		int v = e[i].v;
		if(dis[v] == dis[u] + 1 && (f = dfs(v, min(maxf, e[i].flow))) > 0)
		{
			e[i].flow -= f; e[i ^ 1].flow += f;
			flow += f; maxf -= f; if(!maxf) break;
		}
	}
	return flow;
}

int max_flow()
{
	int fl = 0;
	while(bfs()) fl += dfs(S, inf);
	return fl;
}

int main()
{
	N = read(); M = read();
	S = 0; T = N + M + 1;
	
	for(int i = 1; i <= N; ++i) adde(i, T, read());
	
	for(int i = 1; i <= M; ++i)
	{
		int a = read(), b = read(), c = read();
		adde(i + N, a, inf);
		adde(i + N, b, inf);
		adde(S, i + N, c); tot += c;
	}
	
	printf("%d\n", tot - max_flow());
	
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值