[HIHO119]网络流五·最大权闭合子图(最大流)

时间:2023-03-09 16:07:40
[HIHO119]网络流五·最大权闭合子图(最大流)

题目链接:http://hihocoder.com/contest/hiho119/problem/1

题意:中文题意。

由于1≤N≤200,1≤M≤200。最极端情况就是中间所有边都是满的,一共有N*M条边,则最多有40000条边。

对于这样的问题有统一的建图策略,提取出问题的最终形态即可按照如下方式建图:

首先建立源点s和汇点t,将源点s与所有权值为正的点相连,容量为权值;将所有权值为负的点与汇点t相连,容量为权值的绝对值;权值为0的点不做处理;同时将原来的边容量设置为无穷大。

利用结论:最大权闭合子图的权值等于所有正权点之和减去最小割。

证明:

1. 最小割一定是简单割

简单割指得是:割(S,T)中每一条割边都与s或者t关联,这样的割叫做简单割。

因为在图中将所有与s相连的点放入割集就可以得到一个割,且这个割不为正无穷。而最小割一定小于等于这个割,所以最小割一定不包含无穷大的边。因此最小割一定一个简单割。

2. 简单割一定和一个闭合子图对应

闭合子图V和源点s构成S集,其余点和汇点t构成T集。

首先证明闭合子图是简单割:若闭合子图对应的割(S,T)不是简单割,则存在一条边(u,v),u∈S,v∈T,且c(u,v)=∞。说明u的后续节点v不在S中,产生矛盾。

接着证明简单割是闭合子图:对于V中任意一个点u,u∈S。u的任意一条出边c(u,v)=∞,不会在简单割的割边集中,因此v不属于T,v∈S。所以V的所有点均在S中,因此S-s是闭合子图。

由上面两个引理可以知道,最小割也对应了一个闭合子图,接下来证明最小割就是最大权的闭合子图。

首先有割的容量C(S,T)=T中所有正权点的权值之和+S中所有负权点的权值绝对值之和。

闭合子图的权值W=S中所有正权点的权值之和-S中所有负权点的权值绝对值之和。

则有C(S,T)+W=T中所有正权点的权值之和+S中所有正权点的权值之和=所有正权点的权值之和。

所以W=所有正权点的权值之和-C(S,T)

由于所有正权点的权值之和是一个定值,那么割的容量越小,W也就越大。因此当C(S,T)取最小割时,W也就达到了最大权。

 #include <bits/stdc++.h>
using namespace std; typedef struct Edge {
int u, v, w, next;
}Edge; const int inf = 0x7f7f7f7f;
const int maxn = ; int cnt, dhead[maxn];
int cur[maxn], dd[maxn];
Edge dedge[maxn*maxn];
int S, T, N;
int n, m; void init() {
memset(dhead, -, sizeof(dhead));
for(int i = ; i < maxn; i++) dedge[i].next = -;
cnt = ;
} void adde(int u, int v, int w, int c1) {
dedge[cnt].u = u; dedge[cnt].v = v; dedge[cnt].w = w;
dedge[cnt].next = dhead[u]; dhead[u] = cnt++;
dedge[cnt].u = v; dedge[cnt].v = u; dedge[cnt].w = c1;
dedge[cnt].next = dhead[v]; dhead[v] = cnt++;
} bool bfs(int s, int t, int n) {
queue<int> q;
for(int i = ; i < n; i++) dd[i] = inf;
dd[s] = ;
q.push(s);
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = dhead[u]; ~i; i = dedge[i].next) {
if(dd[dedge[i].v] > dd[u] + && dedge[i].w > ) {
dd[dedge[i].v] = dd[u] + ;
if(dedge[i].v == t) return ;
q.push(dedge[i].v);
}
}
}
return ;
} int dinic(int s, int t, int n) {
int st[maxn], top;
int u;
int flow = ;
while(bfs(s, t, n)) {
for(int i = ; i < n; i++) cur[i] = dhead[i];
u = s; top = ;
while(cur[s] != -) {
if(u == t) {
int tp = inf;
for(int i = top - ; i >= ; i--) {
tp = min(tp, dedge[st[i]].w);
}
flow += tp;
for(int i = top - ; i >= ; i--) {
dedge[st[i]].w -= tp;
dedge[st[i] ^ ].w += tp;
if(dedge[st[i]].w == ) top = i;
}
u = dedge[st[top]].u;
}
else if(cur[u] != - && dedge[cur[u]].w > && dd[u] + == dd[dedge[cur[u]].v]) {
st[top++] = cur[u];
u = dedge[cur[u]].v;
}
else {
while(u != s && cur[u] == -) {
u = dedge[st[--top]].u;
}
cur[u] = dedge[cur[u]].next;
}
}
}
return flow;
} int main() {
// freopen("in", "r", stdin);
int k, u, v, w;
while(~scanf("%d %d", &n, &m)) {
init(); S = ; T = n + m + ; N = T + ;
int pos = ;
for(int i = ; i <= m; i++) {
scanf("%d", &w);
if(w != ) adde(n+i, T, w, );
else adde(n+i, T, inf, );
}
for(int i = ; i <= n; i++) {
scanf("%d %d", &w, &k);
pos += w;
if(w != ) adde(S, i, w, );
else adde(S, i, inf, );
for(int j = ; j < k; j++) {
scanf("%d", &v);
adde(i, n+v, inf, );
}
}
printf("%d\n", pos - dinic(S,T,N));
}
}