给定n个结合
选出k个集合,并且这k个集合并后的大小也为k,求费用最小,
最大权闭合图
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=600+10;
const int M=N*N;
typedef __int64 LL;
const int INF=1e9;
struct Edge{
int v,next,cap;
Edge(){}
Edge(int v,int next ,int cap):v(v),next(next),cap(cap){}
}e[M*2];
int head[N],total;
void init(){
memset(head,-1,sizeof(head));total=0;
}
void adde(int u,int v,int cap){
//printf("%d %d %d\n",u,v,cap);
e[total]=Edge(v,head[u],cap);head[u]=total++;
e[total]=Edge(u,head[v],0);head[v]=total++;
}
int num[N],cur[N],dis[N],pre[N],low[N];
LL isap(int s,int t,int n){
memset(num,0,sizeof(num));num[0]=n;
memset(dis,0,sizeof(dis));
for(int i=0;i<n;i++)cur[i]=head[i];
int u=pre[s]=s;
low[s]=INF;
LL flow=0;
while(dis[s]<n){
loop:
for(int &i=cur[u];i!=-1;i=e[i].next)if(e[i].cap){
int v=e[i].v;
if(dis[v]+1!=dis[u])continue;
low[v]=min(low[u],e[i].cap);
pre[v]=u;
u=v;
if(u==t){
for(u=pre[u];v!=s;v=u,u=pre[u]){
int id=cur[u];
e[id].cap-=low[t];
e[id^1].cap+=low[t];
}
flow+=low[t];
//printf("%d\n",low[t]);
}
goto loop;
}
int mm=n-1;
for(int i=head[u];i!=-1;i=e[i].next)if(e[i].cap){
int v=e[i].v;
if(dis[v]<mm){
cur[u]=i,mm=dis[v];
}
}
if(--num[dis[u]]==0)break;
num[dis[u]=mm+1]++;
u=pre[u];
}
return flow;
}
int aa[N];
int main(){
#ifdef DouBi
freopen("in.cpp","r",stdin);
#endif // DouBi
int n;
while(scanf("%d",&n)!=EOF){
int s=0,t=n*2+1;
init();
for(int i=1;i<=n;i++){
int k;scanf("%d",&k);
while(k--){
int j;scanf("%d",&j);
adde(i,n+j,INF*2);
}
}
LL sum=0;
for(int i=1;i<=n;i++){
scanf("%d",&aa[i]);
adde(0,i,INF-aa[i]);
sum+=INF-aa[i];
}
for(int i=1;i<=n;i++){
adde(n+i,t,INF);
}
LL x=isap(s,t,2*n+2);
x=min((LL)0,-(sum-x));
printf("%I64d\n",x);
}
return 0;
}