bzoj 1497 最小割
思路:最小割好难想啊,根本想不到。。
S -> 用户群 = c[ i ]
基站 -> T = p[ i ]
用户群 -> a[ i ] = inf
用户群 -> b[ i ] = inf
然后求最小割,答案就是全部收益的和 - 最小割。
为什么可以这样呢,对于每个用户群,我们可以不选他,就是把(S -> 用户群)这条边断掉,或者选他,就是把
(用户群 -> a[ i ] = inf ,用户群 -> b[ i ] = inf)就是把这两条边断掉。 这样求最小割就能得到答案。
稍微学了一下最大权闭合图, ans = tot - c (tot 为权值为正的点的和,c 为最小割)。
#include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk make_pair #define pii pair<int, int> using namespace std; const int N = 6e4 + 7; const int M = 2e5 + 7; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const int mod = 1e9 + 7; int n, m, S, T, sum, tot, p[N], a, b, c, level[N], head[N]; struct Edge { int to, w, nx; }edge[M << 1]; void add(int u, int v, int w) { edge[tot].to = v; edge[tot].w = w; edge[tot].nx = head[u]; head[u] = tot++; } bool bfs() { memset(level, 0, sizeof(level)); queue<int> que; que.push(S), level[S] = 1; while(!que.empty()) { int u = que.front(); que.pop(); if(u == T) return true; for(int i = head[u]; ~i; i = edge[i].nx) { int v = edge[i].to, w = edge[i].w; if(level[v] || w <= 0) continue; level[v] = level[u] + 1; que.push(v); } } return false; } int dfs(int u, int p) { if(u == T) return p; int ret = 0; for(int i = head[u]; ~i; i = edge[i].nx) { int v = edge[i].to, w = edge[i].w; if(level[v] != level[u] + 1 || w <= 0) continue; int f = dfs(v, min(p - ret, w)); ret += f; edge[i].w -= f; edge[i ^ 1].w += f; if(ret == p) break; } if(!ret) level[u] = 1; return ret; } int Dinic() { int ans = 0; while(bfs()) ans += dfs(S, inf); return ans; } void init() { tot = 0; sum = 0; memset(head, -1, sizeof(head)); } int main() { init(); scanf("%d%d", &n, &m); S = 0, T = n + m + 1; for(int i = 1; i <= n; i++) { scanf("%d", &p[i]); add(i + m, T, p[i]); add(T, i + m, 0); } for(int i = 1; i <= m; i++) { scanf("%d%d%d", &a, &b, &c); add(S, i, c), add(i, S, 0); add(i, a + m, inf), add(a + m, i, 0); add(i, b + m, inf), add(b + m, i, 0); sum += c; } int ans = Dinic(); printf("%d\n", sum - ans); return 0; } /* */