有两种建图方法,这里采用最大权闭合子图
首先这里基于最大权闭合子图的思想,但是题目中有两种田,而网络流中的s和t有事代表正边权和负边权,所以考虑去掉一个田,我们就假设先全部将种子种在A田然后不断地将种子向B田移。
闭合图基本就是这个样子:
红色是种子,点权可正可负,绿色是B田的组合方式,点权为正,棕色是A田的组合方式,点权为负
1.对于每一个种子,代价为b[i]-a[i]可正可负,按照情况与s或者t连边,边权为|b[i]-a[i]|
2.A的组合与t连边,权值为C[a],所有组成A方式的种子向他连边,边权为inf
3.s与B的组合,权值为C[B],与所有组成的种子两边,边权为inf
#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 2000021
#define inf 0x3fffffff
using namespace std;
int head[maxn],n,m,tot,h[maxn],last[maxn],ans,s,t,q[maxn];
struct edge{int v,next,w;}e[maxn];
void adde(int a,int b,int c){
e[tot].v=b,e[tot].next=head[a],e[tot].w=c;head[a]=tot++;
e[tot].v=a,e[tot].next=head[b],e[tot].w=0;head[b]=tot++;
}
bool bfs(){
for(int i=s;i<=t;i++)h[i]=-1;
int l=0,r=1;q[l]=s;h[s]=0;
while(l<r){
int u=q[l++];
for(int v,i=head[u];i!=-1;i=e[i].next){
if(h[v=e[i].v]==-1&&e[i].w){
h[v]=h[u]+1;
q[r++]=v;
}
}
}
return h[t]!=-1;
}
int dfs(int u,int f){
if(u==t||!f)return f;
int used=0,w;
for(int v,i=last[u];i!=-1;i=e[i].next){
last[u]=i;
if(h[v=e[i].v]!=h[u]+1||!e[i].w)continue;
w=min(e[i].w,f-used);
w=dfs(v,w);
e[i].w-=w,e[i^1].w+=w,used+=w;
if(used==f)return used;
}
if(!used)h[u]=-1;
return used;
}
void dinic(){
while(bfs()){
for(int i=s;i<=t;i++)last[i]=head[i];
ans-=dfs(s,inf);
}
}
int a[maxn],b[maxn];
void build(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",a+i),ans+=a[i];
for(int i=1;i<=n;i++)scanf("%d",&b[i]),b[i]-=a[i];
scanf("%d",&m);t=n+2*m+1;
for(int i=1;i<=n;i++){
if(b[i]>0){
ans+=b[i],adde(s,i,b[i]);
}else adde(i,t,-b[i]);
}
for(int id,k,c1,c2,i=1;i<=m;i++){
scanf("%d%d%d",&k,&c1,&c2);
for(int x,j=1;j<=k;j++){
scanf("%d",&x);
adde(n+i,x,inf);adde(x,n+m+i,inf);
}
ans+=c1,ans+=c2;
adde(s,n+i,c2),adde(n+m+i,t,c1);
}
}
int main(){
memset(head,-1,sizeof(head));
build();
dinic();
printf("%d",ans);
return 0;
}