BZOJ题目传送门
洛谷题目传送门
每条边流量下限为1,上限为 ∞ \infty ∞,建源点汇点向每个点连流量无穷大的边,跑有源汇上下界最小流即可。
对这个的学习可以先看这个dalao博客:有上下界的网络流学习笔记(liu_runda)
推荐看完第一个无源汇之后再配这个的图看一看就比较好理解了有上下界的网络流 学习笔记(Clove_unique)
Code:
#include
#include
#include
#include
#define maxn 105
#define maxm 20005
using namespace std;
const int inf = 0x3f3f3f3f;
int n,m,S,T,ss,tt,tf[maxn];//tf:mintotflow
int fir[maxn],cur[maxn],nxt[maxm],to[maxm],cap[maxm],tot=1;
inline void line(int x,int y,int z){
nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,cap[tot]=z;
nxt[++tot]=fir[y],fir[y]=tot,to[tot]=x,cap[tot]=0;
}
int dis[maxn];
queue<int>q;
bool bfs()
{
memset(dis,0,sizeof dis);
dis[T]=1,q.push(T);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=fir[u];i;i=nxt[i]) if(cap[i^1]&&!dis[to[i]]){
dis[to[i]]=dis[u]+1;
q.push(to[i]);
}
}
return dis[S];
}
int dfs(int u,int lim)
{
if(u==T) return lim;
int need=lim,delta;
for(int &i=cur[u];i;i=nxt[i]) if(cap[i]&&dis[u]==dis[to[i]]+1){
delta=dfs(to[i],min(cap[i],need));
cap[i]-=delta,cap[i^1]+=delta;
if(!(need-=delta)) break;
}
return lim-need;
}
int Dinic(){
int flow=0;
while(bfs()) memcpy(cur,fir,sizeof fir),flow+=dfs(S,inf);
return flow;
}
int main()
{
scanf("%d",&n);
for(int i=1,x;i<=n;i++){
scanf("%d",&m);
while(m--) scanf("%d",&x),tf[i]--,tf[x]++,line(i,x,inf);
//下界流量直接加到tf中,上界限制建边容量为上界-下界
}
S=0,T=n+1,ss=n+2,tt=n+3;
for(int i=1;i<=n;i++){
line(S,i,inf),line(i,T,inf);
if(tf[i]>0) line(ss,i,tf[i]);
else if(tf[i]<0) line(i,tt,-tf[i]);
}
line(T,S,inf);
S=ss,T=tt,Dinic();//虚拟源汇补流
int flow0=cap[tot];//从汇点向源点的反向边流量=源点流出的流量
cap[tot]=cap[tot-1]=0;//删掉T->S的边
S=n+1,T=0;//从汇点向源点跑最大流,相减得到最小流
printf("%d",flow0-Dinic());
}