【图论】最小割

下面用 \((u,v,w)\) 表示节点 \(u\) 到节点 \(v\) 的容量为 \(w\) 的有向边。

最大权闭合子图

最大权闭合子图,指对于某个节点 \(u\) ,若选择节点 \(u\) ,则必须选择节点 \(u\) 可达的所有点集。

对于正权点u,连接S到u,并在答案中默认选择这个正权。对于负权点u,连接u到T,并在答案中默认不选择这个负权。对于选择u则必须选择u可达的点集,暴力的则连接所有可达的点集中的点,事实上若u连接v1,v1连接v2,则u不需要再连接v2(选择层次最高的一个连接就行了)。从答案中减去最小割即可。

验证链接:洛谷P1361 小M的作物

题意:小M有两块耕地A和B,并且有 \(n\) 个种子。小M知道, \(\forall i \in [1,n]\) ,第 \(i\) 个种子种植在耕地A会产生 \(a_i\) 的收益,种植在耕地B会产生 \(b_i\) 的收益。另外,小M发现,有 \(m\) 种作物组合, \(\forall j \in [1,m]\)\(j\) 种作物组合表示为集合 \(S_j\) ,当 \(S_j\) 中的所有种子都种植在耕地A时,会额外产生 \(a_j\) 的收益,当 \(S_j\) 中的所有种子都种植在耕地B时,会额外产生 \(b_j\) 的收益。求最大的收益。

题解:先忽略组合产生的额外收益,就是一个简单的最小割模型:设超级源点 \(S\) 和超级汇点 \(T\) ,对于第 \(i\) 个种子,设一个节点 \(V_i\) ,连接边 \((S,V_i,a_i)\)\((V_i,T,b_i)\) ,加入的总边权就是总收益,然后求一次最小割,得出“割断一些边,使得每个种子最多出现在一块耕地中的最小代价”。(事实上直接贪心也可以)然后尝试加入组合,对于第 \(j\) 个组合,设两个节点 \(Cs_j\)\(Ct_j\) ,连接边 \((S,Cs_j,a_j)\)\((Ct_j,T,b_j)\) ,然后,对于集合 \(S_j\) 中的所有种子 \(i\) ,连接边 \((Cs_j,V_i,\infty)\)\((V_i,Ct_j,\infty)\) ,加入的总边权就是总收益,然后求一次最小割,得出“割断一些边,使得每个种子和组合最多出现在一块耕地中的最小代价”。这样做的正确性,来源于容量为 \(\infty\) 的边不能被割断。要是选择在耕地A种植组合 \(j\) ,那么要割断所有的 \((V_i,T,b_i)\)\((Ct_j,T,b_j)\) ;要是选择在耕地B种植组合 \(j\) ,那么要割断所有的 \((S,V_i,a_i)\)\((S,Cs_j,a_j)\) ;要是选择在耕地A种植一些,在耕地B种植一些,那么把组合的额外边都割断就转化回没有该组合的情形。

这里需要把 \(m\) 种组合拆点,所以非超级点共有 \(n+m+m\) 个,注意边数一定要开够(组合的边,以及反向边)。

int N = n + m + m, S = ++N, T = ++N;
dinic.Init(N, S, T);
ll ans = 0;
for(int i = 1; i <= n; ++i) {
    ll Ai = ?, Bi = ?;
    dinic.AddEdge(S, i, Ai);
    dinic.AddEdge(i, T, Bi);
    ans += Ai, ans += Bi;
}
for(int j = 1; j <= m; ++j) {
    int Csj = n + j, Ctj = n + m + j, k;
    ll Aj = ?, Bj = ?;
    dinic.AddEdge(S, Csj, Aj);
    dinic.AddEdge(Ctj, T, Bj);
    ans += Aj, ans += Bj;
    while(k--) {
        int i = ?;
        dinic.AddEdge(Csj, i, LINF);
        dinic.AddEdge(i, Ctj, LINF);
    }
}
ans -= dinic.Maxflow();
posted @ 2021-01-23 15:34  purinliang  阅读(361)  评论(0编辑  收藏  举报