题解 最大获利

题目传送门

题目大意

给出 (n) 个用户,(m) 个基站,每个用户的需求为用 (a,b) 两个基站,会产生 (c) 的收益。修基站也需要花费。问最大收益。

(nle 5000,mle 50000)

思路

这个东西要用一个叫做最大权闭合子图的东西。大概意思就是求解,对于一个图,每个点有点权,你选了一个点则它的后继都必须选,问选出来的点权值之和最大是多少。对于这个问题我们可以最小割来做,(s) 往所有点权为正的点连,流量为点权,所有点权为负的点往 (t) 连,流量为点权绝对值。原图上的边流量改为 (infty)。然后答案就是所有边权为正的权值之和-最小割。

这个模型的含义就是:

对于 (s) 连向的点,如果割了这个边,相同于不选。

对于连向 (t) 的点,如果割了这个边,相当于选。

正确性和最优性应该显然,应该每个方案都与原图上的方案一一对应。

对于这个点,我们可以直接用户连向基站就好了,用户点权为收益,基站点权为-花费。然后直接套用上面的最大权闭合子图即可。

( exttt{Code})

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define INF 0x3f3f3f3f
#define MAXM 500005
#define MAXN 60005

template <typename T> void read (T &x){char c = getchar ();x = 0;int f = 1;while (c < '0' || c > '9') f = (c == '-' ? -1 : 1),c = getchar ();while (c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar ();x *= f;}
template <typename T,typename ... Args> void read (T &x,Args& ... args){read (x),read (args...);}
template <typename T> void write (T x){if (x < 0) x = -x,putchar ('-');if (x > 9) write (x / 10);putchar (x % 10 + '0');}

int n,m,P[MAXN];

struct edge{
	int v,flw,nxt;
}e[MAXM];
int toop = 1,S,T,dep[MAXN],cur[MAXN],head[MAXN];
void Add_Edge (int u,int v,int w){
	e[++ toop] = edge {v,w,head[u]},head[u] = toop;
	e[++ toop] = edge {u,0,head[v]},head[v] = toop;
}

bool BFS (){
	memset (dep,0,sizeof (dep));	
	queue <int> q;q.push (S),dep[S] = 1;
	while (!q.empty()){
		int u = q.front();q.pop ();
		for (Int i = head[u];i;i = e[i].nxt){
			int v = e[i].v,w = e[i].flw;
			if (!dep[v] && w) dep[v] = dep[u] + 1,q.push (v);
		}
	}
	return dep[T];
}

int dfs (int u,int flow){
	if (u == T) return flow;
	int ans = 0;
	for (Int &i = cur[u];i && flow;i = e[i].nxt){
		int v = e[i].v,f = e[i].flw;
		if (dep[v] != dep[u] + 1) continue;
		int res = dfs (v,min (flow,f));e[i].flw -= res,e[i ^ 1].flw += res,ans += res,flow -= res;
	}
	if (!flow) dep[u] = 0;
	return ans;
}

int MinCut (){
	int res = 0;
	while (BFS ()){
		memcpy (cur,head,sizeof (head));
		res += dfs (S,INF);
	}
	return res;
}

signed main(){
	read (n,m),S = n + m + 1,T = n + m + 2;
	for (Int i = 1;i <= n;++ i) read (P[i]),Add_Edge (m + i,T,P[i]);int ans = 0;
	for (Int i = 1,a,b,c;i <= m;++ i) read (a,b,c),Add_Edge (i,m + a,INF),Add_Edge (i,m + b,INF),Add_Edge (S,i,c),ans += c;
	write (ans - MinCut()),putchar ('
');
	return 0;
}
原文地址:https://www.cnblogs.com/Dark-Romance/p/14008239.html