网络流
最小割
最小割为割掉后使s、t不连通的边的容量和的最小值,数值上等于最大流

inline void add(int a,int b,int c){ e[++tot].t=b;e[tot].flow=c;e[tot].nxt=head[a];head[a]=tot; e[++tot].t=a;e[tot].flow=0;e[tot].nxt=head[b];head[b]=tot; }inline bool bfs(){ memset(dep,0,sizeof(dep)); dep[s]=1; queue<int>q;q.push(s);cur[s]=head[s]; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].nxt){ int v=e[i].t; if(dep[v]||!e[i].flow) continue; dep[v]=dep[u]+1;cur[v]=head[v]; if(v==t) return 1; q.push(v); } }return 0; }int dfs(int u,int flow){ if(u==t) return flow; int rest=flow; for(int &i=cur[u];i&&rest;i=e[i].nxt){ int v=e[i].t; if(dep[v]!=dep[u]+1||!e[i].flow) continue; int tmp=dfs(v,min(rest,e[i].flow)); if(!tmp){dep[v]=0;continue;} rest-=tmp;e[i].flow-=tmp; e[((i-1)^1)+1].flow+=tmp; if(!rest) return flow; }return flow-rest; }inline int Dinic(){ int ans=0; while(bfs()) ans+=dfs(s,inf); return ans; }
总结几种经典建图
1.最大权闭合子图
以下例题极其相似,若只学习套路看例题一即可。
例题1:最大获利
正权点连s,负权点连t,容量为权
原边容量inf,使依赖关系无法分离
ans=正权和-最小割
割去的边为未选择的正权点和已选择的正权点依赖的负权点
以样例为例
1-5中转站,6-10用户群
例题2:植物大战僵尸
正负点与原边关系很明显,注意删去原图的环,环内点以及指向环的点全部不可到达
通过环内点与t连边inf,强制割掉环内正权点
注意网络流中边指向被依赖点,拓扑中边从被依赖点出发
以样例为例
4 5及3排1 2列,成环不可获得。割掉3后答案为230-100-100-5=25

#include<bits/stdc++.h> using namespace std; const int MAX=660; const int inf=1e9; const int N=3e5; int n,m,a[MAX],x,y,z,s,t,head[MAX],tot,dep[MAX],cur[MAX],sum,w; int in[MAX],ans; struct edge{ int t,flow,nxt; } e[N]; inline int read(){ int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } inline void add(int,int,int); inline int bfs(); int dfs(int,int); inline int Dinic(); int main(){ n=read();m=read();t=(s=n*m)+1; for(int i=0;i<n;++i) for(int j=0;j<m;++j){ x=i*m+j;a[x]=read(); if(a[x]>0){ ++in[s];sum+=a[x]; add(s,x,a[x]);add(x,s,0); }else{ ++in[x]; add(x,t,-a[x]);add(t,x,0); } if(j!=m-1){ add(x,x+1,inf);add(x+1,x,0); in[x]++; }w=read(); for(int k=1;k<=w;++k){ z=read()*m+read(); add(z,x,inf);add(x,z,0); ++in[z]; } } queue<int>q; for(int i=0;i<=t;++i) if(!in[i]) q.push(i); while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].nxt){ if(i&1) continue; int v=e[i].t;in[v]--; if(!in[v]) q.push(v); } } for(int i=0;i<s;++i) if(in[i]){ add(i,t,inf);add(t,i,0); } printf("%d\n",sum-Dinic()); } inline void add(int a,int b,int c){ e[++tot].t=b;e[tot].flow=c;e[tot].nxt=head[a];head[a]=tot; }inline int bfs(){ memset(dep,0,sizeof(dep)); queue<int>q;q.push(s);dep[s]=1;cur[s]=head[s]; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].nxt){ int v=e[i].t; if(!e[i].flow||dep[v]) continue; dep[v]=dep[u]+1;cur[v]=head[v]; if(v==t) return 1; q.push(v); } }return 0; }int dfs(int u,int flow){ if(u==t||!flow) return flow; int rest=flow; for(int &i=cur[u];i&&rest;i=e[i].nxt){ int v=e[i].t; if(!e[i].flow||dep[v]!=dep[u]+1) continue; int tmp=dfs(v,min(rest,e[i].flow)); if(!tmp){dep[v]=0;continue;} rest-=tmp;e[i].flow-=tmp; e[((i-1)^1)+1].flow+=tmp; }return flow-rest; }inline int Dinic(){ while(bfs()) ans+=dfs(s,inf); return ans; }
例题3:寿司餐厅
看错题哩,做了一天看题解才发现哩(其实我觉得是题意不明)
区分种类和代号!!!
首先,正权di,j指向di+1,j di,j-1 依赖∑∑dl,r(i<=l<=j,l<=r<=j) 且每个d只贡献一次
其次,负权x每种寿司也只贡献一次,如选择d2,3 d3,4 ,a3只被减一次。可使di,i连边t容量ai
而对于同代号首次贡献mx2,使每个di,i连边代号点,代号点连边t容量mx2
以样例为例
省略了与s,t连边。1为d1,1 2为d1,2 6为d2,1 16、18、19为代号
读清题后,就是简单套路

#include<bits/stdc++.h> using namespace std; const int MAX=20010; const int inf=1e9; const int N=3e5; int n,m,x,a[MAX],mp[110][110],s,t,head[MAX],cur[MAX],tot,dep[MAX],sum,ans; int cnt; struct edge{ int t,flow,nxt; } e[N]; inline int read(){ int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } inline void add(int,int,int); inline int bfs(); int dfs(int,int); inline int Dinic(); int main(){ n=read();m=read(); for(int i=1;i<=n;++i) a[i]=read(); for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) mp[i][j]=++cnt; t=(s=cnt+1000+1)+1; for(int i=1;i<=n;++i) for(int j=i;j<=n;++j){ x=read(); if(x>0) add(s,mp[i][j],x); else add(mp[i][j],t,-x); if(i!=j){ add(mp[i][j],mp[i][j-1],inf); add(mp[i][j],mp[i+1][j],inf); }else{ add(mp[i][i],t,a[i]); add(mp[i][i],cnt+a[i],inf); } } for(int i=1;i<=1000;++i) add(cnt+i,t,m*i*i); printf("%d",sum-Dinic()); } inline void add(int a,int b,int c){ if(a==s) sum+=c; e[++tot].t=b;e[tot].flow=c;e[tot].nxt=head[a];head[a]=tot; e[++tot].t=a;e[tot].flow=0;e[tot].nxt=head[b];head[b]=tot; }inline int bfs(){ memset(dep,0,sizeof(dep)); queue<int>q;q.push(s);dep[s]=1;cur[s]=head[s]; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].nxt){ int v=e[i].t; if(!e[i].flow||dep[v]) continue; dep[v]=dep[u]+1;cur[v]=head[v]; if(v==t) return 1; q.push(v); } }return 0; }int dfs(int u,int flow){ if(u==t||!flow) return flow; int rest=flow; for(int &i=cur[u];i&&rest;i=e[i].nxt){ int v=e[i].t; if(!e[i].flow||dep[v]!=dep[u]+1) continue; int tmp=dfs(v,min(rest,e[i].flow)); if(!tmp){dep[v]=0;continue;} rest-=tmp;e[i].flow-=tmp; e[((i-1)^1)+1].flow+=tmp; }return flow-rest; }inline int Dinic(){ while(bfs()) ans+=dfs(s,inf); return ans; }
2.网格图
以一个图形将几个网格集为一体,一个网格可能多次出现,求最小代价
一般染色,将可能多次出现的网格贡献放在s,t,只出现一次的放在中间。有时也会费用流
例题1:老C的方块
发现黑白染色不能满足要求,于是染4色,对网格编号
奇数列1,2,3,4,1,2,3,4
偶数列4,3,2,1,4,3,2,1
保证每个图形里都有1,2,3,4。发现1,2中间夹着蓝线,即1,2不会同时移除,于是缩为一条边,放在中间。3,4放两边连st,1、2连周围3个3,4
形如

#include<bits/stdc++.h> using namespace std; const int MAX=1e5+10; const int inf=1e9; int C,R,n,s,t; struct block{ int x,y,w; } c[MAX]; struct edge{ int t,flow,nxt; } e[MAX*4]; int dep[MAX],head[MAX],cur[MAX],tot; queue<int>q; map<pair<int,int>,int>mp; inline int read(){ int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();} return x*f; } inline void add(int,int,int); inline bool bfs(); int dfs(int,int); inline int Dinic(); int main(){ // freopen("1.txt","r",stdin); C=read();R=read();n=read(); t=(s=n+1)+1; for(int i=1;i<=n;++i){ c[i]={read(),read(),read()}; mp[{c[i].y,c[i].x}]=i; }for(int i=1;i<=n;++i){ int y=c[i].x,x=c[i].y; if(x&1){ if((y%4==1)&&mp[{x,y+1}]){ add(i,mp[{x,y+1}],min(c[i].w,c[mp[{x,y+1}]].w)); }if(y%4==2){ if(mp[{x,y+1}]) add(i,mp[{x,y+1}],inf); if(mp[{x+1,y}]) add(i,mp[{x+1,y}],inf); if(mp[{x-1,y}]) add(i,mp[{x-1,y}],inf); }if(y%4==3) add(i,t,c[i].w); if(y%4==0){ add(s,i,c[i].w); if(mp[{x,y+1}]) add(i,mp[{x,y+1}],inf); if(mp[{x+1,y}]) add(i,mp[{x+1,y}],inf); if(mp[{x-1,y}]) add(i,mp[{x-1,y}],inf); } }if(!(x&1)){ if(y%4==0&&mp[{x,y-1}]){ add(i,mp[{x,y-1}],min(c[i].w,c[mp[{x,y-1}]].w)); }if(y%4==3){ if(mp[{x,y-1}]) add(i,mp[{x,y-1}],inf); if(mp[{x+1,y}]) add(i,mp[{x+1,y}],inf); if(mp[{x-1,y}]) add(i,mp[{x-1,y}],inf); }if(y%4==2) add(i,t,c[i].w); if(y%4==1){ add(s,i,c[i].w); if(mp[{x,y-1}]) add(i,mp[{x,y-1}],inf); if(mp[{x+1,y}]) add(i,mp[{x+1,y}],inf); if(mp[{x-1,y}]) add(i,mp[{x-1,y}],inf); } } }printf("%d",Dinic()); } inline void add(int a,int b,int c){ e[++tot]={b,c,head[a]};head[a]=tot; e[++tot]={a,0,head[b]};head[b]=tot; }inline bool bfs(){ memset(dep,0,sizeof(dep)); while(!q.empty()) q.pop(); dep[s]=1;q.push(s);cur[s]=head[s]; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].nxt){ int v=e[i].t; if(dep[v]||!e[i].flow) continue; dep[v]=dep[u]+1;cur[v]=head[v]; if(v==t) return 1; q.push(v); } }return 0; }int dfs(int u,int flow){ if(u==t) return flow; int rest=flow; for(int &i=cur[u];i;i=e[i].nxt){ int v=e[i].t; if(dep[v]!=dep[u]+1||!e[i].flow) continue; int tmp=dfs(v,min(rest,e[i].flow)); if(!tmp){dep[v]=0;continue;} rest-=tmp;e[i].flow-=tmp; e[((i-1)^1)+1].flow+=tmp; if(!rest) return flow; }return flow-rest; }inline int Dinic(){ int ans=0; while(bfs()) ans+=dfs(s,inf); return ans; }
3.离散变量模型
例题1:Course Selection
依赖+多选一。很容易想到费用流 ,但进行x到M-x的转换,串联最小割也可以实现多选一。依赖其实就是使A在B之前选择
然后使用最小割离散变量模型:对每个物品的每个状态建店,每个状态向上一个状态连边,流量为此状态的贡献,最后状态向汇点t连无穷边。对于依赖(A,B),源点s向B状态1连无穷边,每个状态i,(A,i)向(B,i+1)连无穷边
以

1 3 2 2 70 100 100 80 100 90 1 2 1 3
为例建图

#include<bits/stdc++.h> using namespace std; const int MAX=10010,inf=1e8; int T,n,m,K,x,y,ans,tot,head[MAX],s,t,dep[MAX],cur[MAX]; struct edge{ int t,nxt,flow; } e[MAX*200]; inline int read(){ int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();} return x*f; } inline void add(int,int,int); inline void Dinic(); signed main(){ freopen("a.in","r",stdin); freopen("a.out","w",stdout); T=read(); while(T--){ n=read();m=read();K=read();t=(s=n*m+1)+1; for(int i=1;i<=t;++i) head[i]=0;tot=0; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){ if(j==1) add(s,i,100-read()); else add((j-2)*n+i,(j-1)*n+i,100-read()); if(j==m) add((j-1)*n+i,t,inf); } for(int i=1;i<=K;++i){ x=read();y=read();add(s,y,inf); for(int j=1;j<m;++j) add((j-1)*n+x,j*n+y,inf); }Dinic();if(ans>100*n) printf("-1\n"); else printf("%d\n",100*n-ans); } } inline void add(int a,int b,int c){ if(c>100) c=inf; // cout<<a<<" "<<b<<" "<<c<<endl; e[++tot].t=b;e[tot].flow=c;e[tot].nxt=head[a];head[a]=tot; e[++tot].t=a;e[tot].flow=0;e[tot].nxt=head[b];head[b]=tot; }inline bool bfs(){ memset(dep,0,sizeof(dep)); dep[s]=1; queue<int>q;q.push(s);cur[s]=head[s]; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].nxt){ int v=e[i].t; if(dep[v]||!e[i].flow) continue; dep[v]=dep[u]+1;cur[v]=head[v]; if(v==t) return 1; q.push(v); } }return 0; }int dfs(int u,int flow){ if(u==t) return flow; int rest=flow; for(int &i=cur[u];i&&rest;i=e[i].nxt){ int v=e[i].t; if(dep[v]!=dep[u]+1||!e[i].flow) continue; int tmp=dfs(v,min(rest,e[i].flow)); if(!tmp){dep[v]=0;continue;} rest-=tmp;e[i].flow-=tmp; e[((i-1)^1)+1].flow+=tmp; if(!rest) return flow; }return flow-rest; }inline void Dinic(){ ans=0; while(bfs()) ans+=dfs(s,inf); }
例题2:切糕
是不是这个题更典
要求相邻点割边距离不超过D的前提下代价最小
在每个物品每个状态建边的基础上,状态i向相邻状态i-D连无穷边
以D=2为例
如果选择距离超过2的蓝边,橙边使得无法形成割

#include<bits/stdc++.h> using namespace std; const int MAX=500060; const int N=1e6+10; const int inf=1<<28; int n,m,head[MAX],tot,cur[MAX],dep[MAX],s,t,ans,sum,cnt; int R,x,D; struct edge{ int t,nxt,flow; } e[MAX]; inline int read(){ int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } inline void add(int,int,int); inline int Dinic(); inline int bfs(); int dfs(int,int); signed main(){ // freopen("in.txt","r",stdin); n=read();m=read();R=read(); D=read();t=(s=n*m*R+1)+1; for(int i=1;i<=R;++i) for(int j=1;j<=n;++j) for(int k=1;k<=m;++k){ x=read();int d=(i-1)*n*m+(j-1)*m+k; if(i==1) add(s,d,inf); if(i!=R) add(d,d+n*m,x); else add(d,t,x); if(i-D<1) continue; int l=d-D*n*m; if(j>1) add(d,l-m,inf); if(k>1) add(d,l-1,inf); if(j<n) add(d,l+m,inf); if(k<m) add(d,l+1,inf);//k<n } printf("%d\n",Dinic()); } inline void add(int a,int b,int c){ e[++tot].flow=c;e[tot].nxt=head[a];e[tot].t=b;head[a]=tot; e[++tot].flow=0;e[tot].nxt=head[b];e[tot].t=a;head[b]=tot; }inline int Dinic(){ ans=0; while(bfs()) ans+=dfs(s,INT_MAX); return ans; }inline int bfs(){ memset(dep,0,sizeof(dep)); queue<int>q;q.push(s); dep[s]=1;cur[s]=head[s]; while(!q.empty()){ int u=q.front(),v;q.pop(); for(int i=head[u];i;i=e[i].nxt){ v=e[i].t; if(e[i].flow<=0||dep[v]) continue; dep[v]=dep[u]+1;cur[v]=head[v]; if(v==t) return 1; q.push(v); } }return 0; }int dfs(int u,int flow){ if(u==t||!flow) return flow; int rest=flow; for(int &i=cur[u];i&&rest;i=e[i].nxt){ int v=e[i].t; if(e[i].flow<=0||dep[v]!=dep[u]+1) continue; int tmp=dfs(v,min(rest,e[i].flow)); if(!tmp){dep[v]=0;continue;} rest-=tmp;e[i].flow-=tmp; e[((i-1)^1)+1].flow+=tmp; }return flow-rest; }
费用流

#include<bits/stdc++.h> using namespace std; const int MAX=210010; const int inf=1<<28; int n,m,s,t,tot,head[410],x,y,z; int dis[MAX],pre[MAX],vis[MAX],ans,cost,flo[MAX]; struct edge{ int t,nxt,flow,val; } e[MAX]; inline int read(){ int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } inline void add(int,int,int,int); inline bool spfa(); inline void EK(); int main(){ n=read();m=read();s=1;t=n; for(int i=2;i<n;++i) add(i,n+i,1,0); for(int i=1;i<=m;++i){ x=read();y=read();z=read(); if(x!=1) x+=n;add(x,y,1,z); }EK(); } inline void add(int a,int b,int c,int v){ e[++tot].t=b;e[tot].flow=c;e[tot].val=v; e[tot].nxt=head[a];head[a]=tot; e[++tot].t=a;e[tot].flow=0;e[tot].val=-v; e[tot].nxt=head[b];head[b]=tot; } inline bool spfa(){ for(int i=1;i<2*n;++i) vis[i]=0,dis[i]=inf; vis[s]=1;dis[s]=0;pre[t]=-1;flo[s]=dis[t]; queue<int>q;q.push(s); while(!q.empty()){ int u=q.front();q.pop(); vis[u]=0; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].t; if(dis[v]>dis[u]+e[i].val&&e[i].flow){ flo[v]=min(flo[u],e[i].flow); pre[v]=i;dis[v]=dis[u]+e[i].val; if(!vis[v]){ vis[v]=1;q.push(v); } } } }return dis[t]!=flo[s]; }inline void EK(){ while(spfa()){ ans+=flo[t];cost+=flo[t]*dis[t]; int now=t; while(now!=s){ e[pre[now]].flow-=flo[t]; e[((pre[now]-1)^1)+1].flow+=flo[t]; now=e[((pre[now]-1)^1)+1].t; } }printf("%d %d\n",ans,cost); }

#include<bits/stdc++.h> using namespace std; const int MAX=400010; #define int long long const int inf=1e17; int dis[MAX],pre[MAX],vis[MAX],ans,cost,flo[MAX],h[MAX];priority_queue<pair<int,int> >q; struct edge{ int t,nxt,flow,val; } e[MAX*20]; inline int read(){ int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } inline void add(int,int,int,int); inline void EK(); inline void add(int a,int b,int c,int v){ // cout<<a<<" "<<b<<" "<<v<<endl; e[++tot].t=b;e[tot].flow=c;e[tot].val=v; e[tot].nxt=head[a];head[a]=tot; e[++tot].t=a;e[tot].flow=0;e[tot].val=-v; e[tot].nxt=head[b];head[b]=tot; } inline bool dij(){ for(int i=1;i<=t;++i) vis[i]=0,dis[i]=inf; dis[s]=0;pre[t]=-1;flo[s]=dis[t]; q.push({0,s}); while(!q.empty()){ int u=q.top().second;q.pop(); if(vis[u]) continue; vis[u]=1; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].t; if(dis[v]>dis[u]+e[i].val+h[u]-h[v]&&e[i].flow){ flo[v]=min(flo[u],e[i].flow); pre[v]=i;dis[v]=dis[u]+e[i].val+h[u]-h[v]; q.push({-dis[v],v}); } } }return dis[t]!=flo[s]; }inline void EK(){ while(dij()){ for(int i=1;i<=t;++i) h[i]+=dis[i]; ans+=flo[t];cost+=flo[t]*h[t]; int now=t; while(now!=s){ e[pre[now]].flow-=flo[t]; e[((pre[now]-1)^1)+1].flow+=flo[t]; now=e[((pre[now]-1)^1)+1].t; } }printf("%lld\n",cost); }
上下界
无源汇有上下界可行流

#include<bits/stdc++.h> using namespace std; const int MAX=21010; const int inf=1e9; #define int long long int n,m,T,q,tot,s,t,tf[MAX],head[MAX],cur[MAX],dep[MAX],x,y,z,low[MAX],sum,ans[MAX]; struct edge{ int t,flow,nxt; } e[MAX]; inline int read(){ int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();} return x*f; } inline void add(int,int,int); inline bool bfs(); int dfs(int,int); inline int Dinic(); signed main(){ n=read();m=read(); t=(s=n+1)+1; for(int i=1;i<=m;++i){ x=read();y=read();low[i]=read();z=read(); add(x,y,z-low[i]);tf[x]-=low[i];tf[y]+=low[i]; }sum=0; for(int i=1;i<=n;++i){ if(tf[i]<0) add(i,t,-tf[i]); else{ sum+=tf[i];add(s,i,tf[i]); } }if(Dinic()==sum){ printf("YES\n"); for(int i=1;i<=n;++i) for(int j=head[i];j;j=e[j].nxt){ if(j>m*2||(j&1)) continue; ans[j>>1]=e[j].flow+low[j>>1]; } for(int i=1;i<=m;++i) printf("%d\n",ans[i]); }else printf("NO\n"); } inline void add(int a,int b,int c){ e[++tot].t=b;e[tot].flow=c;e[tot].nxt=head[a];head[a]=tot; e[++tot].t=a;e[tot].flow=0;e[tot].nxt=head[b];head[b]=tot; }inline bool bfs(){ memset(dep,0,sizeof(dep));dep[s]=1; queue<int>q;q.push(s);cur[s]=head[s]; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].nxt){ int v=e[i].t; if(dep[v]||!e[i].flow) continue; dep[v]=dep[u]+1;cur[v]=head[v]; if(v==t) return 1; q.push(v); } }return 0; }int dfs(int u,int flow){ if(u==t) return flow; int rest=flow; for(int &i=cur[u];i&&rest;i=e[i].nxt){ int v=e[i].t; if(dep[v]!=dep[u]+1||!e[i].flow) continue; int tmp=dfs(v,min(rest,e[i].flow)); if(!tmp){dep[v]=0;continue;} rest-=tmp;e[i].flow-=tmp; e[((i-1)^1)+1].flow+=tmp; }return flow-rest; }inline int Dinic(){ int ans=0; while(bfs()) ans+=dfs(s,inf); return ans; }
有源汇有上下界最大流

#include<bits/stdc++.h> using namespace std; const int MAX=21010; const int inf=1e9; #define int long long int n,m,T,tot,s,t,tf[MAX],head[MAX],cur[MAX],dep[MAX],x,y,z,low[MAX],sum,ans[MAX]; int ss,tt,res,qs,qt; struct edge{ int t,flow,nxt; } e[MAX]; inline int read(){ int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();} return x*f; } inline void add(int,int,int); inline bool bfs(); int dfs(int,int); inline int Dinic(); signed main(){ n=read();m=read();s=read();t=read(); tt=(ss=n+1)+1; for(int i=1;i<=m;++i){ x=read();y=read();low[i]=read();z=read(); add(x,y,z-low[i]);tf[x]-=low[i];tf[y]+=low[i]; }sum=0; for(int i=1;i<=n;++i){ if(tf[i]<0) add(i,tt,-tf[i]); else{ sum+=tf[i];add(ss,i,tf[i]); } }add(t,s,inf);qs=s;qt=t; s=ss;t=tt; if(Dinic()==sum){ s=qs;t=qt; printf("%d\n",Dinic()); }else printf("please go home to sleep\n"); } inline void add(int a,int b,int c){ e[++tot].t=b;e[tot].flow=c;e[tot].nxt=head[a];head[a]=tot; e[++tot].t=a;e[tot].flow=0;e[tot].nxt=head[b];head[b]=tot; }inline bool bfs(){ memset(dep,0,sizeof(dep));dep[s]=1; queue<int>q;q.push(s);cur[s]=head[s]; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].nxt){ int v=e[i].t; if(dep[v]||!e[i].flow) continue; dep[v]=dep[u]+1;cur[v]=head[v]; if(v==t) return 1; q.push(v); } }return 0; }int dfs(int u,int flow){ if(u==t) return flow; int rest=flow; for(int &i=cur[u];i&&rest;i=e[i].nxt){ int v=e[i].t; if(dep[v]!=dep[u]+1||!e[i].flow) continue; int tmp=dfs(v,min(rest,e[i].flow)); if(!tmp){dep[v]=0;continue;} rest-=tmp;e[i].flow-=tmp; e[((i-1)^1)+1].flow+=tmp; }return flow-rest; }inline int Dinic(){ int res=0; while(bfs()) res+=dfs(s,inf); return res; }
有源汇有上下界最小流

#include<bits/stdc++.h> using namespace std; const int MAX=125010; const int N=5e4+10; const int inf=1e9; //#define int long long int n,m,T,tot,s,t,tf[N],head[N],cur[N],dep[N],x,y,z,low[MAX],sum; int ss,tt,qs,qt; struct edge{ int t,flow,nxt; } e[MAX+N<<1]; inline int read(){ int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();} return x*f; } inline void add(int,int,int); inline bool bfs(); int dfs(int,int); inline int Dinic(); inline void del(int x){ for(int i=head[x];i;i=e[i].nxt) e[i].flow=e[((i-1)^1)+1].flow=0; } signed main(){ n=read();m=read();s=read();t=read(); tt=(ss=n+1)+1; for(int i=1;i<=m;++i){ x=read();y=read();low[i]=read();z=read(); add(x,y,z-low[i]);tf[x]-=low[i];tf[y]+=low[i]; }sum=0; for(int i=1;i<=n;++i){ if(tf[i]<0) add(i,tt,-tf[i]); else{ sum+=tf[i];add(ss,i,tf[i]); } }add(t,s,inf);qs=s;qt=t; s=ss;t=tt; if(Dinic()==sum){ s=qt;t=qs;sum=e[tot].flow; e[tot].flow=e[tot-1].flow=0; del(ss);del(tt); printf("%d\n",sum-Dinic()); }else printf("please go home to sleep\n"); } inline void add(int a,int b,int c){ e[++tot].t=b;e[tot].flow=c;e[tot].nxt=head[a];head[a]=tot; e[++tot].t=a;e[tot].flow=0;e[tot].nxt=head[b];head[b]=tot; }inline bool bfs(){ memset(dep,0,sizeof(dep)); dep[s]=1; queue<int>q;q.push(s);cur[s]=head[s]; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].nxt){ int v=e[i].t; if(dep[v]||!e[i].flow) continue; dep[v]=dep[u]+1;cur[v]=head[v]; if(v==t) return 1; q.push(v); } }return 0; }int dfs(int u,int flow){ if(u==t) return flow; int rest=flow; for(int &i=cur[u];i&&rest;i=e[i].nxt){ int v=e[i].t; if(dep[v]!=dep[u]+1||!e[i].flow) continue; int tmp=dfs(v,min(rest,e[i].flow)); if(!tmp){dep[v]=0;continue;} rest-=tmp;e[i].flow-=tmp; e[((i-1)^1)+1].flow+=tmp; if(!rest) return flow; }return flow-rest; }inline int Dinic(){ int ans=0; while(bfs()) ans+=dfs(s,inf); return ans; }
有源汇上下界最小费用可行流

#include<bits/stdc++.h> using namespace std; const int MAX=30010; const int N=4010; bool vis[N]; int dis[N],pre[N],ss,tt,p,m,f,M,S,n,tot,s,t,head[N],x; const int inf1=1e9; #define ll long long const ll inf=1e17; struct edge{ int t,nxt,val; ll flow; } e[MAX]; ll flo[N],ans,cost; queue<int>q; inline int read(){ int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();} return x*f; } inline void add(int,int,ll,int); inline bool spfa(); inline void EK(); signed main(){ // freopen("1.txt","r",stdin); n=read();t=(s=n<<1|1)+1; tt=(ss=t+1)+1; for(int i=1;i<=n;++i){ x=read();add(i,n+i,inf,0); add(i+n,t,inf,0); add(i,tt,x,0);add(ss,n+i,x,0); }p=read();m=read();f=read();M=read();S=read(); add(s,1,inf,p); for(int i=1;i<=n;++i){ if(i!=n) add(i,i+1,inf,0); if(i+m<=n) add(n+i,i+m,inf,f); if(i+M<=n) add(n+i,i+M,inf,S); }add(t,s,inf,0); s=ss;t=tt;EK(); printf("%lld",cost); } inline void add(int a,int b,ll c,int v){ e[++tot].t=b;e[tot].flow=c;e[tot].val=v;e[tot].nxt=head[a];head[a]=tot; e[++tot].t=a;e[tot].flow=0;e[tot].val=-v;e[tot].nxt=head[b];head[b]=tot; }inline bool spfa(){ for(int i=1;i<=t;++i) vis[i]=0,dis[i]=inf1; vis[s]=1;dis[s]=0;pre[t]=-1;flo[s]=inf; q.push(s); while(!q.empty()){ int u=q.front();q.pop();vis[u]=0; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].t; if(dis[v]>dis[u]+e[i].val&&e[i].flow){ flo[v]=min(flo[u],e[i].flow); pre[v]=i;dis[v]=dis[u]+e[i].val; if(!vis[v]){vis[v]=1;q.push(v);} } } }return dis[t]!=inf1; }inline void EK(){ while(spfa()){ ans+=flo[t];cost+=1ll*flo[t]*dis[t]; int now=t; while(now!=s){ e[pre[now]].flow-=flo[t]; e[((pre[now]-1)^1)+1].flow+=flo[t]; now=e[((pre[now]-1)^1)+1].t; } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律