這題是最大權閉合子圖的模板題。
閉合子圖:所有點的後繼都在圖內。
模型:
有一些正權點,負權點。
每個正權點有一些後繼。
你需要求一個最大權閉合子圖。
做法:正權點值和最小割
證明:
給二分圖加入源點,匯點。
正權點與S的連邊的容量爲其權值。
負權點與T的連邊的容量爲其權值的相反數。
正權點和其後繼的連邊的容量爲.
跑完網絡流後,那麼殘餘網絡上與S相連的部分的點權和即爲答案。
解釋:由於中間的邊爲,所以一定不會被割掉,跟S相連的部分的,也就是獲得收益。(割邊都對應負權點)
而和T相連的部分就是不會得到收益。(割邊都對應正權點)。
有一個更方便的做法是:正權點值和最小割。就是後一部分不計入答案。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110,inf=2e9;
int n,m,d[N],q[N],ans,st,ed;
struct edge{int y,next,c;}a[N*N]; int len=1,last[N],cur[N],b[N];
void ins(int x,int y,int c) {a[++len]=(edge){y,last[x],c}; last[x]=len;}
void add(int x,int y,int c) {ins(x,y,c); ins(y,x,0);}
bool bfs() {
memset(d,0,(ed+1)<<2);
int l,r; q[l=r=1]=st; d[st]=1;
while(l<=r) {
int x=q[l++];
for(int k=last[x],y;k;k=a[k].next) {
y=a[k].y;
if(!d[y]&&a[k].c) {
d[y]=d[x]+1;
q[++r]=y;
if(y==ed) return 1;
}
}
}
return 0;
}
int dfs(int x,int f) {
if(x==ed)return f;
int s=0,t;
for(int k=cur[x],y,z;k&&s<f;k=a[k].next) {
y=a[k].y; z=a[k].c;
if(d[y]==d[x]+1&&z>0) {
s+=(t=dfs(y,min(f-s,z)));
a[k].c-=t; a[k^1].c+=t; cur[x]=k;
}
}
if(!s) d[x]=0;
return s;
}
int main() {
scanf("%d %d",&n,&m); m+=n; st=0; ed=m+1;
for(int i=1,x;i<=n;i++) {
scanf("%d",&x); add(st,i,x); ans+=x;
while(getchar()==' ') scanf("%d",&x),add(i,x+n,inf);
}
for(int i=n+1,x;i<=m;i++) scanf("%d",&x),add(i,ed,x);
while(bfs()) memcpy(cur,last,(ed+1)<<2),ans-=dfs(st,inf);
for(int i=1;i<=n;i++) if(d[i]) b[++b[0]]=i;
for(int i=1;i<=b[0];i++) printf("%d%c",b[i]," \n"[i==b[0]]);
b[0]=0;
for(int i=n+1;i<=m;i++) if(d[i]) b[++b[0]]=i-n;
for(int i=1;i<=b[0];i++) printf("%d%c",b[i]," \n"[i==b[0]]);
printf("%d\n",ans); return 0;
}