活动 - AcWing
新的技术正冲击着手机通讯市场,对于各大运营商来说,这既是机遇,更是挑战。
THU 集团旗下的 CS&T 通讯公司在新一代通讯技术血战的前夜,需要做太多的准备工作,仅就站址选择一项,就需要完成前期市场研究、站址勘测、最优化等项目。
在前期市场调查和站址勘测之后,公司得到了一共 N 个可以作为通讯信号中转站的地址,而由于这些地址的地理位置差异,在不同的地方建造通讯中转站需 要投入的成本也是不一样的,所幸在前期调查之后这些都是已知数据:
建立第 i 个通讯中转站需要的成本为 Pi(1≤i≤N)。
另外公司调查得出了所有期望中的用户群,一共 M 个。
关于第 i 个用户群的信息概括为 Ai,Bi 和 Ci:这些用户会使用中转站 Ai 和中转站 Bi 进行通讯,公司可以获益 Ci。(1≤i≤M,1≤Ai,Bi≤N)
THU 集团的 CS&T 公司可以有选择的建立一些中转站(投入成本),为一些用户提供服务并获得收益(获益之和)。
那么如何选择最终建立的中转站才能让公司的净获利最大呢?(净获利 = 获益之和 – 投入成本之和)
输入格式
第一行有两个正整数 N 和 M。
第二行中有 N 个整数描述每一个通讯中转站的建立成本,依次为 P1,P2,…,PN 。
以下 M 行,第 (i+2) 行的三个数 Ai,Bi 和 Ci 描述第 i 个用户群的信息。
所有变量的含义可以参见题目描述。
输出格式
输出一个整数,表示公司可以得到的最大净获利。
数据范围
1≤N≤5000,
1≤M≤50000,
0≤Ci,Pi≤100
输入样例:
5 5
1 2 3 4 5
1 2 3
2 3 4
1 3 3
1 4 2
4 5 3
输出样例:
4
样例解释
选择建立 1、2、31、2、3 号中转站,则需要投入成本 66,获利为 1010,因此得到最大收益 44。
解析:
有若干个中转站,建立每个中转站都需要花费对应的成本。我们还有若干个用户群,每个用户群连接两个中转站,我们要向满足某个用户群的需求,就必须建立对应的两个中转站,因此我们可以从用户群向对应的两个中转站分别连一条有向边,表示如果想满足这个用户群的需求就必须要两个中转站建立起来。而满足每个用户群的需求都能获得对应的收益。
本题要求最大获利,获利就是总收益减去总成本。可以发现成本都是要减去的,收益都是要增加的。因此如果将所有中转站和用户群看作节点,那么所有中转站的权值就是负的,所有用户群的权值就是正的,选取任何一个用户群,都要算上对应的两个中转站,即从该用户群出发能到的所有点都需要选上,因此在选点的时候其实就是在选闭合点集,而我们的目标是使选的所有点的权值和最大,其实就是让我们求整张图的最大权闭合子图。
因此本题只需要按照求最大权闭合子图的步骤去求即可,首先是建图,从源点向所有正权点连边,容量就是权值;从所有负权点向汇点连边,容量就是权值的绝对值;所有用户群向需要建造的中转站连边,容量是 +∞
最大权闭合图相关概念(作者:小小_88 ):最大权闭合子图的基本概念 - AcWing
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
#include<sstream>
#include<deque>
#include<unordered_map>
#include<unordered_set>
#include<bitset>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 55e3 + 5, M = (5e4*3 + 5e3) * 2 + 10, INF = 0x3f3f3f3f;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];void add(int a, int b, int c) {e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx++;e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx++;
}bool bfs() {int hh = 0, tt = 0;memset(d, -1, sizeof d);q[0] = S, d[S] = 0, cur[S] = h[S];while (hh <= tt) {int t = q[hh++];for (int i = h[t]; i != -1; i = ne[i]) {int j = e[i];if (d[j] == -1 && f[i]) {d[j] = d[t] + 1;cur[j] = h[j];if (j == T)return 1;q[++tt] = j;}}}return 0;
}int find(int u, int limit) {if (u == T)return limit;int flow = 0;for (int i = cur[u]; i != -1 && flow < limit; i = ne[i]) {int j = e[i];cur[u] = i;if (d[j] == d[u] + 1 && f[i]) {int t = find(j, min(f[i], limit - flow));if (!t)d[j] = -1;f[i] -= t, f[i ^ 1] += t, flow += t;}}return flow;
}int dinic() {int ret = 0, flow;while (bfs())while (flow = find(S, INF))ret += flow;return ret;
}int main() {cin >> n >> m;memset(h, -1, sizeof h);S = 0, T = n + m + 1;for (int i = 1,a; i <= n; i++) {scanf("%d", &a);add(i + m, T, a);}int tot = 0;for (int i = 1,a,b,c; i <= m; i++) {scanf("%d%d%d", &a, &b, &c);add(S, i, c);add(i, a + m, INF);add(i, b + m, INF);tot += c;}printf("%d\n", tot - dinic());return 0;
}