我的网络流7题
最大流2题:
洛谷P2756 飞行员配对方案问题
分析
其实就是一个二分图匹配求最大匹配数的问题,加一个源点和汇点,再跑一遍网络流,输出方案的时候检查一下有没有流经过即可(反向边是否非0)。
注意:每个点在向源点和汇点连边时,要在输入完之后再连。否则会重复连。
#include<bits/stdc++.h> using namespace std; #define N 205 #define M 30005 int to[M],head[N],nex[M],w[M],lev[N],tot=1,cnt=0,inf=0x7f7f7f,s=0,t=101,n,cur[N]; struct node{ int u,v,bian; }e[M]; queue<int> q; void add(int a,int b,int ww) {tot++; to[tot]=b; w[tot]=ww; nex[tot]=head[a]; head[a]=tot;if(ww==0&&b&&a!=t) e[cnt].bian=tot; } bool bfs() {memset(lev,-1,sizeof(lev));lev[s]=0; q.push(s);while(!q.empty()){int u=q.front(); q.pop();for(int i=head[u];i!=-1;i=nex[i]){int v=to[i];if(w[i]>0&&lev[v]==-1)lev[v]=lev[u]+1,q.push(v);}}if(lev[t]!=-1) return true;return false; } int dfs(int u,int flow) {if(u==t) return flow;int ret=flow;for(int i=cur[u];i!=-1;i=nex[i]){cur[u]=i;if(ret<=0) break;int v=to[i];if(w[i]>0&&lev[u]+1==lev[v]){int k=dfs(v,min(w[i],ret));ret-=k; w[i]-=k; w[i^1]+=k; //异或运算:2k^1=(2k+1) (2k+1)^1=2k; }}return flow-ret;//最大限制 - 还剩的多少没流 =流出去了多少 } void dinic() {int ans=0;while(bfs()){for(int i=0;i<=101;i++) cur[i]=head[i]; ans+=dfs(s,inf);}printf("%d\n",ans); } int main() {int m,a,b;scanf("%d%d",&m,&n);memset(head,-1,sizeof(head));while(1){scanf("%d%d",&a,&b);if(a==-1&&b==-1) break;cnt++;add(a,b,inf); add(b,a,0); e[cnt].u=a; e[cnt].v=b;}//一定要在后面建,否则会重复建边 for(int i=1;i<=m;i++) add(s,i,1),add(i,s,0);for(int i=m+1;i<=n;i++) add(i,t,1),add(t,i,0);dinic();for(int i=1;i<=cnt;i++)if(w[e[i].bian]!=0) printf("%d %d\n",e[i].u,e[i].v); } /* 5 10 1 7 1 8 2 6 2 9 2 10 3 7 3 8 4 7 4 8 5 10 -1 -12 4 1 3 1 4 2 3 -1 -1 */
洛谷P2765 魔术球问题
«问题描述:
假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可放11 个球。
n<=55
分析
首先,这道题的主要限制在于球(平方数),考虑对球建边。将每个球拆成两部分,第一部分对 s 连边,第二部分对 t 连边, 两部分间通过和为平方数的条件连边(边权都为1)。一个一个地加球,加一次跑一次最大流,如果在以前的基础上产生了新流,说明加入的这个球能对已有的球产生平方和的联系,即可以加在某一个球上面。加球一直到没有产生新流了,说明需要加柱子了,这时就将柱子++,一直到达到n个为止。
方案输出
每当新加一个柱子时,记录最先放入柱子的球,然后在找增广路的时候对每一个点,记录下一个可行的点是谁,类似于邻接表一样的链表式结构。然后输出的时候枚举柱子。
#include<bits/stdc++.h> using namespace std; #define inf 2100000000 #define M 1000005 int to[M],nex[M],head[M],w[M],tot=0,vis[M]; int nexxt[M],headd[M],lev[M],cur[M],s=M-5,t=M-4; queue<int> q; void add(int a,int b,int ww) {to[++tot]=b; nex[tot]=head[a]; head[a]=tot; w[tot]=ww;to[++tot]=a; nex[tot]=head[b]; head[b]=tot; w[tot]=0; } bool bfs() {memset(lev,-1,sizeof(lev));lev[s]=0; q.push(s);while(!q.empty()){int u=q.front(); q.pop();for(int i=head[u];i!=-1;i=nex[i]){int v=to[i];if(lev[v]==-1 && w[i]) lev[v]=lev[u]+1,q.push(v);}}return lev[t]!=-1; } int dfs(int u,int flow) {if(u==t) return flow;int ret=flow;for(int i=head[u];i!=-1;i=nex[i]){int v=to[i];if(ret<=0) break;if(lev[v]==lev[u]+1&&w[i]>0){int k=dfs(v,min(ret,w[i]));nexxt[u>>1]=v>>1;//记录一下这个球对应的下一个球是哪一个 因为存的时候*2+1了 所以要/2 不需要分奇偶讨论 ret-=k; w[i]-=k; w[i^1]+=k;}} return flow-ret; } int dinic() {int ans=0;while(bfs()){ans+=dfs(s,inf);} return ans; } int main() {int n;scanf("%d",&n);memset(head,-1,sizeof(head));int now=0,zhu=0;// headd[1]=1;while(zhu<=n){now++;add(s,now<<1,1); add(now<<1|1,t,1);for(int i=sqrt(now)+1;i*i<now*2;i++)add((i*i-now)<<1,now<<1|1,1);int tmp=dinic();if(!tmp) headd[++zhu]=now;}printf("%d\n",now-1);for(int i=1;i<=n;i++)if(!vis[headd[i]]){for(int j=headd[i];j&&j!=t>>1;j=nexxt[j])printf("%d ",j),vis[j]=true;printf("\n");} }
#include<bits/stdc++.h> using namespace std; #define inf 2100000000 #define M 1000005 int to[M],nex[M],head[M],w[M],tot=0,vis[M]; int nexxt[M],headd[M],lev[M],cur[M],s=M-5,t=M-4; queue<int> q; void add(int a,int b,int ww) {to[++tot]=b; nex[tot]=head[a]; head[a]=tot; w[tot]=ww;to[++tot]=a; nex[tot]=head[b]; head[b]=tot; w[tot]=0; } bool bfs() {memset(lev,-1,sizeof(lev));lev[s]=0; q.push(s);while(!q.empty()){int u=q.front(); q.pop();for(int i=head[u];i!=-1;i=nex[i]){int v=to[i];if(lev[v]==-1 && w[i]) lev[v]=lev[u]+1,q.push(v);}}return lev[t]!=-1; } int dfs(int u,int flow) {if(u==t) return flow;int ret=flow;for(int i=head[u];i!=-1;i=nex[i]){int v=to[i];if(ret<=0) break;if(lev[v]==lev[u]+1&&w[i]>0){int k=dfs(v,min(ret,w[i]));nexxt[u>>1]=v>>1;//记录一下这个球对应的下一个球是哪一个 因为存的时候*2+1了 所以要/2 不需要分奇偶讨论 ret-=k; w[i]-=k; w[i^1]+=k;}} return flow-ret; } int dinic() {int ans=0;while(bfs()){ans+=dfs(s,inf);} return ans; } int main() {int n;scanf("%d",&n);memset(head,-1,sizeof(head));int now=0,zhu=0;//柱子要从0开始!!因为第一次进去时一定最大流为 0 headd[1]=1;while(zhu<=n){now++;add(s,now<<1,1); add(now<<1|1,t,1); //now < i*i < 2*now 这样保证了i*i-now为正 且是小的数向大的数连边 for(int i=sqrt(now)+1;i*i<now*2;i++)add((i*i-now)<<1,now<<1|1,1);int tmp=dinic();if(!tmp) headd[++zhu]=now;//headd记录了每个柱子最下面的球是谁,然后依次跳链表遍历整个柱子里面的球 }printf("%d\n",now-1);for(int i=1;i<=n;i++)if(!vis[headd[i]]){//如果这个柱子的第一个球还没有被遍历 说明这个柱子还没被遍历 for(int j=headd[i];j&&j!=t>>1;j=nexxt[j])printf("%d ",j),vis[j]=true;printf("\n");} }
最小割2题:
洛谷P2774方格取数问题
题意:
取了一个格子,就不能取有公共边的格子,求能取出的最大值。
分析
思路:取了一种点 就不能取另外一种点 对于这样的限制关系 可以将对立的条件进行连边
通过将边权设为inf的方式 限制不能同时选 也就是跑最大流(=最小割 )
最小割模板其实是要一部分的点向源点连边,一部分向汇点连边的形式
但是这道题我全部的点都向源汇点连了,为什么没有错呢?
是因为有一个边权的限制在里面 使其可以跑对,而下一道题骑士共存就不行了
#include<bits/stdc++.h> using namespace std; #define inf 2100000000 #define N 20005 #define M 1000005 int to[M],head[N],nex[M],w[M],tot=1,cnt=0,sum=0,lev[N],n,m; //为了不与已有的点重复 nn=n*m 注意扩展域的范围 int id[102][102],dx[5]={0,0,1,-1},dy[5]={1,-1,0,0},s=0,t=20001,nn=10000,aa[102][102]; queue<int> q; void add(int a,int b,int ww) {to[++tot]=b; nex[tot]=head[a]; head[a]=tot; w[tot]=ww;to[++tot]=a; nex[tot]=head[b]; head[b]=tot; w[tot]=0; } bool bfs() {memset(lev,-1,sizeof(lev));lev[s]=0; q.push(s);while(!q.empty()){int u=q.front(); q.pop();for(int i=head[u];i!=-1;i=nex[i]){int v=to[i];if(lev[v]==-1&&w[i]>0) lev[v]=lev[u]+1,q.push(v);}}return lev[t]!=-1; } int dfs(int u,int flow) {if(u==t) return flow;int ret=flow;for(int i=head[u];i!=-1;i=nex[i]){int v=to[i];if(ret<=0) break;if(lev[v]==lev[u]+1&&w[i]>0){int k=dfs(v,min(ret,w[i]));ret-=k; w[i]-=k; w[i^1]+=k;}}return flow-ret; } void dinic() {int ans=0;while(bfs()) ans+=dfs(s,inf);//printf("%d\n",ans);printf("%d\n",sum-ans/2); } int main() {scanf("%d%d",&m,&n);memset(head,-1,sizeof(head));for(int i=1;i<=m;i++)for(int j=1;j<=n;j++){scanf("%d",&aa[i][j]);id[i][j]=++cnt;sum+=aa[i][j];}for(int i=1;i<=m;i++)for(int j=1;j<=n;j++){add(s,id[i][j],aa[i][j]);add(id[i][j]+nn,t,aa[i][j]);for(int k=0;k<=3;k++){int x=dx[k]+i,y=dy[k]+j;if(x<1||x>m||y>n||y<1) continue;add(id[i][j],id[x][y]+nn,inf);}}dinic(); }
洛谷P3355 骑士共存问题
题意:
在一个位置放一个骑士,其周围有八个位置都不能放,还有障碍也不能放,求最多能放多少个骑士?
分析
与方格取数类似,但一定要一部分连接源点,一部分连汇点,不能所以的点都连源汇点
如果所有的都连了源汇点,就会发现跑出来与s t相连的边都是满流的,并没有起作用(因为边权值都为1)
一部分连,一部分不连,就可以跑出正确的最小割了
一部分与另一部分的划分 是坐标之和的奇偶性 所以也叫奇偶建图
为什么是坐标之和的奇偶呢:
oxoxo
xoxox
oxoxo
如果我们将所有点分成O和X两种点,我们可以发现同种点无法互相攻击
那我们可以把所有点分成两个点集,分别为O点的点集和X的点集,然后在两个点集有某些点对无法全选
做法:
先创一个源点和汇点
让源点到所有O点(即横纵坐标加起来为奇数的点)连一条容量为1的边
让所有X点(即横纵坐标加起来为偶数的点)到汇点连一条容量为1的边
再对无法同时选择的O点和X点,让O点连一条到X点容量为inf的点
最后跑最大流,设为最大流为x,输出n∗n−m−x就好了
(部分摘自洛谷第一篇题解)
#include<bits/stdc++.h> using namespace std; #define inf 2100000000 #define N 80005 #define M 1000005 int to[M],head[N],nex[M],w[M],tot=1,cnt=0,sum=0,lev[N],n,m,cur[N]; int id[202][202],dx[8]={-2,-1,-2,-1,2,1,2,1},dy[8]={1,2,-1,-2,1,2,-1,-2}; int s=0,t=40001,nn=40000,c[202][202]; queue<int> q; void add(int a,int b,int ww) {to[++tot]=b; nex[tot]=head[a]; head[a]=tot; w[tot]=ww;to[++tot]=a; nex[tot]=head[b]; head[b]=tot; w[tot]=0; } bool bfs() {memset(lev,-1,sizeof(lev));for(int i=s;i<=t;i++) cur[i]=head[i];lev[s]=0; q.push(s);while(!q.empty()){int u=q.front(); q.pop();for(int i=head[u];i!=-1;i=nex[i]){int v=to[i];if(lev[v]==-1&&w[i]>0) lev[v]=lev[u]+1,q.push(v);}}return lev[t]!=-1; } int dfs(int u,int flow) {if(u==t) return flow;int ret=flow;for(int i=cur[u];i!=-1;i=nex[i]){cur[u]=i;int v=to[i];if(ret<=0) break;if(lev[v]==lev[u]+1&&w[i]>0){int k=dfs(v,min(ret,w[i]));ret-=k; w[i]-=k; w[i^1]+=k;}}return flow-ret; } void dinic() {int ans=0;while(bfs()) ans+=dfs(s,inf); // printf("%d\n",ans);printf("%d\n",n*n-m-ans); } int main() {int a,b;scanf("%d%d",&n,&m);memset(head,-1,sizeof(head));for(int i=1;i<=m;i++) scanf("%d%d",&a,&b),c[a][b]=1;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){id[i][j]=++cnt;if(c[i][j]) continue;if((i+j)&1) add(s,id[i][j],1); //奇偶建图:奇数点在左边 else { add(id[i][j],t,1); continue; }}for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){if(((i+j)&1)==0||c[i][j]) continue;//障碍跳过 奇偶建图:偶数点在右边 不进行连边 for(int k=0;k<=7;k++){int x=dx[k]+i,y=dy[k]+j;if(x<1||y<1||x>n||y>n||c[x][y]) continue;add(id[i][j],id[x][y],inf);}}dinic(); }
费用流2题
洛谷P2045 方格取数加强版
题意:现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0
这样一共走K次,现在要求K次所达到的方格的数的和最大
反向边的费用置为-w的原因:当反悔的时候 说明不获取这条边的费用 就走反向边 将原来获取的减去
建边:先拆点拆成入点x和出点x'
x与x'连费用为格子上的数 容量为 1的边 :表示当第一次走的时候可以获取费用
x与x'再连一条费用为0 容量为 k-1的边 :表示再走k-1次将不再获取费用
x'再向下和右的入点连费用为0 容量为 k的边:表示走k次 不收获费用
注意:入点被别人连边 而出点向别人连边
#include<bits/stdc++.h> using namespace std; #define N 5005 #define M 1000005 int to[M],head[N],nex[M],w[M],fl[M],tot=1,flow[N],dis[N],vis[N],last[N],pre[N];//tot=1!! int s,t,nn=2500,max_flow=0,min_cost=0,id[55][55]; queue<int> q; void add(int a,int b,int ww,int ff) {to[++tot]=b; nex[tot]=head[a]; head[a]=tot; w[tot]=ww; fl[tot]=ff;to[++tot]=a; nex[tot]=head[b]; head[b]=tot; w[tot]=-ww; fl[tot]=0;//反向边的费用置为负的 } //spfa原理:在残量网络上 将费用 w 视作边权 跑最短路 bool spfa() {//费用流的前提都是保证最大流下再考虑费用 //而在此题里面 流的限制使得能够恰好取k次 memset(vis,0,sizeof(vis));//一定要记得都要memset memset(dis,0x7f7f7f,sizeof(dis));memset(flow,0x7f7f7f,sizeof(flow));dis[s]=0; vis[s]=1; q.push(s);while(!q.empty()){int u=q.front(); q.pop(); vis[u]=0;for(int i=head[u];i!=-1;i=nex[i]){int v=to[i];if(fl[i]>0 && dis[v]>dis[u]+w[i]){dis[v]=dis[u]+w[i];flow[v]=min(flow[u],fl[i]);//flow[v]表示流到v这个点时 经过的流量限制是多少 last[v]=i; pre[v]=u;//last记录边的编号 pre记录点的前驱 if(!vis[v]) q.push(v),vis[v]=1;}}}return dis[t]!=2139062143; } void solve() {while(spfa()){int now=t;//从终点往回跳 跳到起点 max_flow+=flow[t];min_cost+=flow[t]*dis[t];//最小费用的定义 是容量与距离相乘 while(now!=s){fl[last[now]]-=flow[t];fl[last[now]^1]+=flow[t];now=pre[now];}} } int main() {int x,n,k,cnt=0;scanf("%d%d",&n,&k);memset(head,-1,sizeof(head));for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)id[i][j]=++cnt;s=1; t=cnt+nn;//注意这里的s t一定要设置成格子的左上方和右下方这两个点的入点和出点!! //因为图中是没有额外向起点或终点连边的! for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){//printf("id:%d\n",id[i][j]);scanf("%d",&x); x=-x;//取负数将最大费用转化成最小费用 用spfa跑最短路 //将点转化成边:拆点->拆成入点和出点->若要经过这个点 就必经入点与出点连接的这条边//第一次经过费用为原权值 多次经过则为0 经过k次的限制转化成边的容量 让网络流限制其恰好经过k次 add(id[i][j],id[i][j]+nn,x,1); add(id[i][j],id[i][j]+nn,0,k-1);if(i<n) add(id[i][j]+nn,id[i+1][j],0,k);//出边连向其它点 其它点连向入边 if(j<n) add(id[i][j]+nn,id[i][j+1],0,k);}solve();printf("%d\n",-min_cost); } /* 3 2 1 2 3 0 2 1 1 4 2 */
洛谷P1251 餐巾计划问题
关键是如何建边 ,然后跑最小费用最大流。
建边方法:将一个点视作餐厅的一天,拆成两部分:早上和晚上。 s视作纸巾的来源,t视作纸巾的去处。下面开始连边。
每天晚上花费0得到早上用过的纸巾:s -> i+n (0)
每天早上花费0用掉当天所需的纸巾:i ->t (0)
每天早上花费p重新买纸巾:s ->i (p)
每天晚上的脏纸巾可以花费快洗的费用送到后m天使用:i+n -> i+m (kf)
慢洗同理
每天用过的脏纸巾不一定今天洗,可以连向每天晚上洗:i+n -> i+n+1 (0)
#include<bits/stdc++.h> using namespace std; #define N 4005 #define M 1000005 #define ll long long #define inf 2100000000 int to[M],nex[M],head[N],w[M],fl[M],vis[N],flow[N],last[N],pre[N]; int tot=1,s=0,t=4001; ll dis[N],min_cost=0; queue<int> q; void add(int a,int b,int ww,int ff) {to[++tot]=b; nex[tot]=head[a]; head[a]=tot; w[tot]=ww; fl[tot]=ff;to[++tot]=a; nex[tot]=head[b]; head[b]=tot; w[tot]=-ww; fl[tot]=0; } bool spfa() {memset(vis,0,sizeof(vis));memset(dis,0x7f7f7f,sizeof(dis));//注意int的 0x7f7f7f和 long long的不一样 !! memset(flow,0x7f7f7f,sizeof(flow));dis[s]=0; q.push(s); vis[s]=1;while(!q.empty()){int u=q.front(); q.pop(); vis[u]=0;//!!!一定要加 否则就打了个假的spfa for(int i=head[u];i!=-1;i=nex[i]){int v=to[i];if(fl[i]>0 && dis[v]>dis[u]+w[i]){dis[v]=dis[u]+w[i];flow[v]=min(fl[i],flow[u]);last[v]=i; pre[v]=u;if(!vis[v]) q.push(v),vis[v]=1;}}}return dis[t]!=9187201950435737471; } void solve() {while(spfa()){int now=t;min_cost+=dis[t]*flow[t];while(now!=s){fl[last[now]]-=flow[t];fl[last[now]^1]+=flow[t];now=pre[now];}} } int main() {int n,p,m,mf,k,kf,x;scanf("%d",&n);memset(head,-1,sizeof(head));//i代表每天早上 i+n代表每天晚上 //用掉纸巾向 t连边 得到被s连边 for(int i=1;i<=n;i++){scanf("%d",&x);add(i,t,0,x); add(s,i+n,0,x);//每天早上用掉 x张纸巾 每天晚上得到 x张脏的纸巾 } scanf("%d%d%d%d%d",&p,&k,&kf,&m,&mf);for(int i=1;i<=n;i++){if(i+1<=n) add(i+n,i+1+n,0,inf);//留到第二天晚上洗 if(i+m<=n) add(i+n,i+m,mf,inf);//慢洗:今天晚上送去洗 m天后的早上得到 可以送去任意条 if(i+k<=n) add(i+n,i+k,kf,inf);//同理 add(s,i,p,inf);//每天早上可以从s那里通过p的代价重新购买 }solve();printf("%lld\n",min_cost); } /* 3 1 7 5 11 2 2 3 12 1 7 11 1 3 3 1 */
我写错的地方:spfa可以重复入队,所以出队后应该清除标记!!
最小路径覆盖1题:
洛谷P2764 最小路径覆盖问题
题意:
求最小路径覆盖条数,并输出方案。
最小路径覆盖:
n个点的无向图,最多需要n条路径覆盖(每个点覆盖它自己)。但其实可以一条路径的尾节点与另一条路径的首节点相连,合并成一条路径。这样每合并一次,路径条数就会减少一条。于是就将问题转化成了:最多能合并多少条路径。然后开始建图:s -> i , i+n -> t (1) 。然后原图中有边的相互连边,最后跑最大流即可。
ans=n-最大流
方案输出:
在dfs里面记录一个点下一个点是谁,在输出方案的时候像跳链表一样依次输出。但要注意的是每次要找到一条路径的端点(通过打vis标记),才能正确输出。
两种dfs写法不一样会导致答案也不一样??
#include<bits/stdc++.h> using namespace std; #define N 305 #define M 1000005 int ans=0,tot=1,n,m,s=0,t=301; int to[M],head[N],nex[M],w[M],lev[N],cur[N],vis[N],nextt[N],fl[N]; queue<int> q; void add(int a,int b,int ww) {to[++tot]=b; nex[tot]=head[a]; w[tot]=ww; head[a]=tot;to[++tot]=a; nex[tot]=head[b]; w[tot]=0; head[b]=tot; } bool bfs() {memset(lev,-1,sizeof(lev));for(int i=s;i<=t;i++) cur[i]=head[i];lev[s]=0; q.push(s);while(!q.empty()){int u=q.front(); q.pop();for(int i=head[u];i!=-1;i=nex[i]){int v=to[i];if(lev[v]==-1&&w[i]>0) lev[v]=lev[u]+1,q.push(v);}}return lev[t]!=-1; } int dfs( int now, int flow) {if(now==t)return flow;for(int &i=cur[now];i;i=nex[i]){cur[now]=i;int qw=to[i];if(w[i]>0&&lev[qw]==lev[now]+1){int kk=dfs(qw,min(flow,w[i]));if(kk>0){nextt[now]=qw;//记录一个点下一个点是谁 便于方案输出 if(now!=s) vis[qw-n]=1;//打标记找到一条路径的起点 最后没有被标记的就是起点 w[i]-=kk,w[i^1]+=kk;return kk;}}}return 0; } /*int dfs(int u,int flow)另一种写法是错的。。。 {if(u==t) return flow;int ret=flow;for(int i=cur[u];i!=-1;i=nex[i]){cur[u]=i;int v=to[i];if(ret<=0) break;if(w[i]>0&&lev[v]==lev[u]+1){int k=dfs(v,min(ret,w[i]));if(u!=s) vis[v-n]=true;//!!nextt[u]=v;ret-=k; w[i]-=k; w[i^1]+=k;}}return flow-ret; }*/ void dinic() {while(bfs()) ans+=dfs(s,2100000000); } int main() {scanf("%d%d",&n,&m);memset(head,-1,sizeof(head));for(int i=1;i<=n;i++) add(s,i,1),add(i+n,t,1);int a,b;for(int i=1;i<=m;i++){scanf("%d%d",&a,&b);add(a,b+n,1);}dinic();for(int i=1;i<=n;i++)if(!vis[i]){int now=i;printf("%d ",now);while(nextt[now]&&nextt[now]!=t){printf("%d ",nextt[now]-n);now=nextt[now]-n;}printf("\n");} printf("%d\n",n-ans); } /* 11 9 1 2 1 3 1 4 2 5 3 6 4 7 6 9 7 10 8 114 3 4 1 1 2 2 3 */