把集合和元素建二分图连边,根据Hall定理,此图存在完美匹配。求出一组完美匹配。
接下来考虑怎样的选法是合法的。可以发现,如果选择了
k
个集合,那么我们至少选择了匹配边连到的
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=200000,oo=0x3f3f3f3f;
vector<int> e[maxn],a[maxn];
int w[maxn],to[maxn],val[maxn],f[maxn],g[maxn],dep[maxn],que[maxn],n,num,s,t;
int dfs(int u,int lim)
{
int ret=0,v,x;
if (u==t) return lim;
for (vector<int>::iterator it=e[u].begin();it!=e[u].end()&&ret<lim;++it)
if (w[*it]&&dep[v=to[*it]]==dep[u]+1)
{
x=dfs(v,min(lim-ret,w[*it]));
ret+=x;
w[*it]-=x;
w[*it^1]+=x;
}
if (ret<lim) dep[u]=0;
return ret;
}
int bfs()
{
int hd=1,tl=1,u,v;
for (int i=0;i<=t;i++) dep[i]=0;
que[1]=s;
dep[s]=1;
while (hd<=tl)
{
u=que[hd++];
for (vector<int>::iterator it=e[u].begin();it!=e[u].end();++it)
if (w[*it]&&!dep[v=to[*it]])
{
dep[v]=dep[u]+1;
que[++tl]=v;
}
}
return dep[t];
}
void add(int u,int v,int x)
{
num++;
e[u].push_back(num*2);
w[num*2]=x;
to[num*2]=v;
e[v].push_back(num*2+1);
w[num*2+1]=0;
to[num*2+1]=u;
}
int main()
{
//freopen("c.in","r",stdin);
int x,y,ans=0;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
while (x--)
{
scanf("%d",&y);
a[i].push_back(y);
add(i,y+n,1);
}
}
for (int i=1;i<=n;i++) scanf("%d",&val[i]);
s=2*n+1;
t=2*n+2;
for (int i=1;i<=n;i++)
{
add(s,i,1);
add(i+n,t,1);
}
while (bfs()) dfs(s,oo);
for (int i=1;i<=n;i++)
for (vector<int>::iterator it=e[i].begin();;++it)
if (!w[*it]&&to[*it]!=s)
{
f[i]=to[*it]-n;
g[to[*it]-n]=i;
break;
}
for (int i=0;i<=t;i++) e[i].clear();
num=0;
for (int i=1;i<=n;i++)
{
if (val[i]>0) add(i,t,val[i]);
else
{
ans+=val[i];
add(s,i,-val[i]);
}
for (vector<int>::iterator it=a[i].begin();it!=a[i].end();++it)
if (*it!=f[i]) add(i,g[*it],oo);
}
while (bfs()) ans+=dfs(s,oo);
printf("%d\n",ans);
}