上下界网络流。
建源点s,汇点t,s向每个点连0-inf的边,每个点向t连0-inf的边,(u,v)间连1-inf的边,然后上下界最小流。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
queue<int> q;
const int inf=0x7f7f7f7f;
int n,m,tot,head[115],s,t,ss,tt,dis[115],ans;
struct node
{
int from;
int to;
int next;
int w;
}edge[300005];
void add(int u,int v,int w)
{
edge[tot].from=u;
edge[tot].to=v;
edge[tot].w=w;
edge[tot].next=head[u];
head[u]=tot++;
}
bool bfs()
{
q.push(ss);
memset(dis,0,sizeof(dis));
dis[ss]=1;
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=head[x];i!=-1;i=edge[i].next)
{
if(!dis[edge[i].to]&&edge[i].w)
{
dis[edge[i].to]=dis[x]+1;
q.push(edge[i].to);
}
}
}
if(!dis[tt])
return 0;
return 1;
}
int dfs(int x,int flow)
{
if(x==tt||!flow)
return flow;
int ret=0;
for(int i=head[x];i!=-1;i=edge[i].next)
{
if(dis[edge[i].to]==dis[x]+1)
{
int f=dfs(edge[i].to,min(edge[i].w,flow));
ret+=f;flow-=f;
edge[i].w-=f;edge[i^1].w+=f;
if(!flow)
break;
}
}
if(!ret)
dis[x]=0;
return ret;
}
void dinic()
{
while(bfs())
{
ans+=dfs(ss,0x7f7f7f7f);
}
}
void ins(int u,int v,int low,int high)
{
add(ss,v,low);
add(v,ss,0);
add(u,tt,low);
add(tt,u,0);
add(u,v,high-low);
add(v,u,0);
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
int x;
s=0,t=n+1,ss=n+2,tt=n+3;
for(int i=1;i<=n;i++)
{
add(s,i,inf);
add(i,s,0);
add(i,t,inf);
add(t,i,0);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&m);
for(int j=1;j<=m;j++)
{
scanf("%d",&x);
ins(i,x,1,inf);
}
}
dinic();
ans=0;
add(t,s,inf);
add(s,t,0);
dinic();
cout<<ans;
}