有源汇有上下界最小流
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<queue>
#define INF 2e9
#define N 110
#define M 12500
using namespace std;
int read()
{
int a=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}
return a*f;
}
struct edge{int to,next,v;}e[M*2];
int n,m,cnt,sum,s,t,ss,tt;
int a[N],d[N],head[N],cur[N];
void add(int x,int y,int v)
{
e[cnt].next=head[x],e[cnt].to=y,e[cnt].v=v;
head[x]=cnt++;
e[cnt].next=head[y],e[cnt].to=x,e[cnt].v=0;
head[y]=cnt++;
}
int bfs()
{
queue<int>q;
memset(d,-1,sizeof(d));
q.push(ss),d[ss]=0;
while(!q.empty())
{
int x=q.front();q.pop();
if(x==tt) {memcpy(cur,head,sizeof(cur));return 1;}
for(int i=head[x];i!=-1;i=e[i].next)
if(d[e[i].to]==-1&&e[i].v)
{
d[e[i].to]=d[x]+1;
q.push(e[i].to);
}
}
return 0;
}
int dfs(int x,int a)
{
if(x==tt||!a) return a;
int flow=0,f;
for(int &i=cur[x];i!=-1;i=e[i].next)
if(d[e[i].to]==d[x]+1&&(f=dfs(e[i].to,min(a,e[i].v)))>0)
{
e[i].v-=f;
e[i^1].v+=f;
flow+=f;
a-=f;
if(!a) break;
}
if(!flow) d[x]=-1;
return flow;
}
int dinic()
{
int res=0;
while(bfs())
res+=dfs(ss,INF);
return res;
}
int main()
{
n=read();
s=0,t=n+1,ss=n+2,tt=n+3;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;++i)
add(s,i,INF),add(i,t,INF);
for(int i=1,m;i<=n;++i)
{
m=read();
while(m--)
{
int j=read();
add(i,j,INF); //每个地方至少去一次即下界为1
--a[i],++a[j];
}
}
for(int i=1;i<=n;++i)
if(a[i]>0)
sum+=a[i],add(ss,i,a[i]);
else if(a[i]<0)
add(i,tt,-a[i]);
add(t,s,INF);
sum-=dinic();
sum=e[head[t]^1].v;
head[s]=e[head[s]].next,head[t]=e[head[t]].next;
ss=t,tt=s;
printf("%d\n",sum-dinic());
return 0;
}
偷偷水一篇