网络流总结

我的网络流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
*/
View Code

 

洛谷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");}
}
View Code

 

#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();
}
View Code

洛谷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();
}
View Code

 

费用流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
*/
View Code

 洛谷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
*/
View Code

 

我写错的地方: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
*/
View Code

 

转载于:https://www.cnblogs.com/mowanying/p/11341451.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/568978.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JMeter如何切换成中文详细步骤

首先&#xff0c;如图打开JMeter&#xff0c;点击Options(选项)&#xff0c;再见下拉选项就选中Choose Language(选中语言)&#xff0c;最后再点击Chinese(Simplified) (中文简体的意思) https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信…

JMeter 入门脚本以发送百度请求为例

1. 打开JMeter 就会有一个默认的Test Plan(测试计划) 2. 添加线程组 https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&am…

Python中的回文详解

回文&#xff1a; https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 让这个可爱的宝藏女孩在努力的道路上与你一起同行&#xff01; 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c…

TypeError object of type ‘type‘ has no len()—Python报错问题:

翻译过来是类型为“type”的TypeError对象没有len&#xff08;&#xff09;&#xff0c;我报错的代码是&#xff1a; https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 让这个可爱的宝藏女孩在努力的…

NameError: name ‘c‘ is not defined--python报错问题

翻译的意思是&#xff1a;名称错误&#xff1a;未定义名称“c” 报错代码是&#xff1a; #codingutf-8print("请输入一个字符串&#xff1a;") a input() #回文的长度至少为2 if len(c) < 2:print(请不要输入空字符串&#xff01;)a input(请重新输入一个字符串…

Python中的pip怎么配置环境变量

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xff09; Python3.4以上版本的&#xff0c;系统自带pip。 打开Pytho…

c#中struct和class的区别

简单来说&#xff0c;struct是值类型&#xff0c;创建一个struct类型的实例被分配在栈上。class是引用类型&#xff0c;创建一个class类型实例被分配在托管堆上。但struct和class的区别远不止这么简单。本篇主要包括&#xff1a; ■ 概括来讲■ 从赋值的角度体验struct和class的…

利用xlrd,Python对excel读取文件

Python安装xlrd 首先我们应该配置pip 详细配置教程地址&#xff1a; https://blog.csdn.net/hanhanwanghaha/article/details/105811544 https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 让这个可爱…

利用openpyxl,Python对excel读写文件

Python安装openpyxl 首先我们应该配置pip 详细配置教程地址&#xff1a; https://blog.csdn.net/hanhanwanghaha/article/details/105811544 电脑打开cmd,输入&#xff1a; pip install openpyxl 如下图&#xff1a; 如上图&#xff0c;就安装好了 随后我们打开pycharm&…

ASP.NET简易教程3——SQL存储过程

SQL存储过程 B/S结构下&#xff0c;离不开数据库&#xff0c;在接下来的学习时&#xff0c;要求读者至少知道少许的基本SQL语句&#xff08;增删查改&#xff09;。 在进行正式编程时&#xff0c;简要介绍一下存储过程。存储过程就是将一系列的SQL语句集中置一个文件中。 在大型…

Python中的openpyxl如何对excel修改文件

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xff09; Python安装openpyxl 首先我们应该配置pip 详细配置教程地…

IndentationError: unindent does not match any outer indentation level-报错问题

在python中遇到这样的问题 翻译过来是&#xff1a; 缩进错误&#xff1a;未缩进与任何外部缩进级别都不匹配 很显然&#xff0c;一般都是格式没对&#xff0c;你看看你的代码对齐了没有。python对缩进非常的严格呢&#xff01; 例如&#xff1a; 此处我的j多打了一个空格&am…

冒泡排序详解--python

我没学冒泡排序之前眼中的冒泡排序是这个样子的 哈哈哈 https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 让这个可爱的宝藏女孩在努力的道路上与你一起同行&#xff01; 如有转载&#xff0c;请注明…

uni-app和php交互DES加密解密数据

1 uni-app操作 (1) 打开HBuilderX的视图->显示终端 cd 切换到你项目的根目录 执行命令 npm install crypto-js 安装成功后你的项目根目录会生成node_modules文件夹&#xff0c;里面有crypto-js就说明成功了 &#xff08;2&#xff09;接下来在哪使用就直接import CryptoJS f…

字典超详细--python

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 让这个可爱的宝藏女孩在努力的道路上与你一起同行&#xff01; 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xf…

两数之和-给定一个整数数组nums和一个目标值target,请你在该数组找出和为目标值的那两个整数,并返回他们的数组下标,你可以假设每种输入只会对应一个答案。但是,数组同一个元素不能使-python

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 让这个可爱的宝藏女孩在努力的道路上与你一起同行&#xff01; 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xf…

选择排序算法python

选择排序定义&#xff1a; https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 让这个可爱的宝藏女孩在努力的道路上与你一起同行&#xff01; 如有转载&#xff0c;请注明出处&#xff08;如不注明&am…

Python中的异常处理Try...except...finally的使用的简单理解

python中的异常处理1.Python中的异常处理定义2.Try…except…finally的使用3.代码4.运行结果1.Python中的异常处理定义 在程序运行的过程中&#xff0c;如果发生了错误就会报错&#xff0c;我们可以事先约定返回一个错误代码&#xff0c;这样就可以知道是否有错&#xff0c;以…

Aveva Marine 新建项目001

1# 项目代号定义&#xff0c;三个字符&#xff0c;例如Abc 2# 新建文件夹&#xff0c;命名为“Abc” 3# 新建文件名为evars.bat文件&#xff0c;放到项目文件夹的根目录 内容为&#xff1a; 1 SET Abc000项目文件夹路径\Abc000\ 2 SET AbcMAC项目文件夹路径\AbcMAC\ 3 SET …

在CSDN写文章头部生成标题目录

步骤&#xff1a; 1.点击帮助&#xff0c;再点击目录。 2.随后出现目录&#xff0c;再点击复制。 3.把你复制的东西粘贴到文章开头&#xff0c;将你想要作为目录的文字设置成为标题。注意&#xff1a;前面有几个井号键就是几级标题。 5.标题目录设置好之后&#xff0c;就可…