spoj1476 maximum profit,最大权闭合子图

http://www.spoj.com/problems/PROFIT/


最大权闭合子图:点权之和最大的闭合图。


建图:每一条有向边变为容量为inf,源S到正权点v( wv>0 )的边容量 wv ,负权点v( wv<0 )到汇 T 的边容量 wv ,零权点v( wv=0 )不与源和汇相连。然后求最小割(SUM-最大流)即为答案。

/*
 * Author: yew1eb
 * Created Time:  2014年10月31日 星期五 15时39分22秒
 * File Name: spoj1476 maximum profit.cpp
 */
#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
const int inf = 1e9;
const ll  INF = 1e18;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int maxn = 60010;
const int maxm = 2000010;

struct Edge {
    int to, cap, next;
    Edge(int _next=0,int _to=0,int _cap=0):next(_next),to(_to),cap(_cap) {}
} edge[maxm];

int n, m, S, T, cnt;
int head[maxn], tot;
int d[maxn];//到汇点的距离下界。
int gap[maxn];

void init()
{
    tot = 0;
    memset(head, -1, sizeof head );
}

void add(int u,int v,int c, int rc=0)
{
    edge[tot] = Edge(head[u], v, c);
    head[u] = tot++;
    edge[tot] = Edge(head[v], u, rc);
    head[v] = tot++;
}


int get_flow(int u, int flow)
{
    if(u==T || flow==0)return flow;
    int res=0, f;
    for(int i=head[u]; ~i; i=edge[i].next) {
        int &v = edge[i].to;
        if(d[u]>d[v] && (f=get_flow(v,min(flow,edge[i].cap)))>0) {
            edge[i].cap -= f;
            edge[i^1].cap += f;
            res += f;
            flow -= f;
            if(flow==0) return res;
        }
    }
    if(!(--gap[d[u]]))d[S]=cnt+2;
    gap[++d[u]]++;
    return res;
}

int isap()
{
    int flow = 0;
    memset(gap, 0, sizeof gap );
    memset(d, 0, sizeof d );
	cnt = T-S+1;
    gap[0] = cnt;
    while(d[S]<cnt) flow += get_flow(S, inf);
    return flow;
}


int main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
   // freopen("out.cpp", "w", stdout);
#endif // ONLINE_JUDGE
	int t;
	int n, m;
	scanf("%d", &t);
	while(t--){
		scanf("%d%d", &n, &m);
		S = 0, T = n+m+1;
		int u, v, c;
		init();
		for(int i=1; i<=n; ++i){
			scanf("%d", &c);
			add(i, T, c);
		}
		int sum = 0;
		for(int i=n+1; i<=n+m; ++i){
			scanf("%d%d%d", &u, &v, &c);
			add(S, i, c);
			sum += c;
			add(i, u, inf);
			add(i, v, inf);
		}	
		int ans = sum - isap();
		printf("%d\n", ans);
	}
	return 0;
}





















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值