P1361 小M的作物(最小割好题)
题目大意:给我们一些节点,我们需要把这些节点分为两部分,节点位于两种区域中的价值不同,而且
还会有一些组合,这些组合位于不同的区域中价值也不同。
思路:本蒟蒻没想出来....看的题解。
印象比较深刻的话:最小割在数值上与最大流相等,但在本身性质上与网络流无任何关系。
我的理解(可能会很抽象....):最小割可以抽象为两个对立的势力一样,要么位于A,要么位于B,位于A有一定的
好处,位于B也有一定的好处,而我们把这些好处当作容量连边,把x想象成节点,连边A->x,权值为加入
A的好处,然后连边x->B,权值设置为加入B的好处,这样跑最大流,虽然用的最大流算法,但是得到的却是x位于A,B中的最小那个权值
也就是最小割,然后我们把所有节点位于A,B的权值和-最小割,就是权值最大的安排。
然后组合呢?其实这题的组合也可以是一个节点x,我们只需要把组合也虚拟为两个节点,然后A与其中一个节点连边,
权值设置为组合在A下的得分,再将该节点与组合中的节点连边,大小设置为inf,同理,我们再把另一个节点与B连边,
权值设置为B下的得分,再把组合中的节点与其连边,权值设置为inf,这样也就把组合也抽象为了两种对立的势力下的得分。
假设这题的组合不是一个组合在A,B下同时有得分,列如组合(1,2,3)只有在A下的得分x1,组合(2,3,4)只有在B下的得分x2,这样
就样就没法形成对立的势力,没法用最小割解决(大概),口胡结束.
本蒟蒻刚开始接触网络流,欢迎巨佬指点我错误的地方^_^
不好意思,只知道自己理解了,忘记提交了,结果有个小错误,已改正。
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 600005; const int inf = 0x3f3f3f3f; struct edge { int f, t, nxt; ll flow; }e[maxn * 2]; int hd[maxn], tot = 1; void add(int f, int t, ll flow) { e[++tot] = { f,t,hd[f],flow }; hd[f] = tot; } int n, m, c1, c2; int s, d; int dep[maxn], cur[maxn]; bool bfs() {//找增广路 memset(dep, 0, sizeof(dep)); dep[s] = 1; queue<int>Q; Q.push(s); while (!Q.empty()) { int u = Q.front(); Q.pop(); for (int i = hd[u]; i; i = e[i].nxt) { int v = e[i].t, flow = e[i].flow; if (flow > 0 && !dep[v]) { dep[v] = dep[u] + 1; if (v == d)return true; Q.push(v); } } } return false; } ll dfs(int u, ll flow) { if (u == d)return flow; ll last = flow; for (int i = cur[u]; i; i = e[i].nxt) { cur[u] = i;//当前弧优化 int v = e[i].t; ll flow = e[i].flow; if (flow > 0 && dep[v] == dep[u] + 1) { ll tmp = dfs(v, min(last, flow)); last -= tmp; e[i].flow -= tmp; e[i ^ 1].flow += tmp; if (last == 0)break;//流量没了直接退出循环,与当前弧优化对应 } } if (last == flow)dep[u] = 0;//从当前点一点流量没流到终点(未找到增广路),炸点优化 return flow - last;//返回剩余流量 } ll dinic() { ll maxflow = 0; while (bfs()) { memcpy(cur, hd, sizeof(hd)); maxflow += dfs(s, inf); } return maxflow; } int main() { //freopen("test.txt", "r", stdin); scanf("%d", &n); ll sum = 0; s = n + 1, d = n + 2; for (int i = 1; i <= n; i++) { ll x; scanf("%lld", &x); sum += x; add(s, i, x),add(i, s, 0); } for (int i = 1; i <= n; i++) { ll x; scanf("%lld", &x); sum += x; add(i, d, x); add(d, i, 0); } scanf("%d", &m); for (int i = 1; i <= m; i++) { int t;ll wa, wb; scanf("%d%lld%lld", &t, &wa, &wb); sum += wa + wb; add(s, n + 2 + i, wa), add(n + 2 + i, s, 0);//拆点连边 add(n + 2 + m + i, d, wb), add(d, n + 2 + m + i, 0); for (int k = 1; k <= t; k++) { int x; scanf("%d", &x); add(n + 2 + i, x, inf), add(x, n + 2 + i, 0); add(x, n + 2 + m + i, inf), add(n + 2 + m + i, x, 0); } } ll mincut = dinic(); printf("%lld\n", sum - mincut); return 0; }