一.无源汇上下界可行流
题目:
我们考虑一个事情:将上界减去下界作为使得每条边满足条件,这样我们就构建出了一个满足容量限制的网络图;
但是我们似乎没有满足流量平衡,简单的举个例子:设流入u点的流量为f1,流出为f2,那么当f1!=f2,我们构建的图是存在问题的,因为u在我们构建的图中,每一条边的容量都被重新定义了,我们默认了他们都流过了最小流量。但是这个最小流量却是不守恒的;
尝试修补我们的漏洞,我们虚构两个点,源点s,汇点t,我们尝试构建补偿流,
1.对于流入大于流出(f1>f2),我们考虑一下我们构建的图的意义,当在新图中的点u,当流入为a的流量时,实际的流量为a+f1,实际的流出流量为f2+a;可以知道我们流出的少了,假设我们使得流出的a变为b,b=a+f1-f2;那么我们就需要连一条边s->u(边权为f1-f2);
2.对于流入小于流出(f1<f2),当在新图中的点u,当流入为a的流量时,实际的流量为a+f1,实际的流出流量为f2+a;可以知道我们流出的多了,那么我们需要一条边来进行分担流出的,那么我们就需要连一条边u->t(边权为f2-f1);
我们可以知道的一点是,源点和汇点的边权和tot是一样的,当汇点和源点的边权全部流满时,才会对应于原图的可行流(因为我们需要满足补偿),所以我们采用dinic求最大流,看最大流是否为tot
实现代码如下:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N=210,M=(10210+N)*2,INF=1e9;
int n,m,S,T;
int h[N],ne[M],e[M],idx,f[M],C[M];
int s[N],d[N],cur[N];
void add(int a, int b, int c, int d){
e[idx]=b;f[idx]=d-c;C[idx]=c;ne[idx]=h[a];h[a]=idx++;
e[idx]=a;f[idx]=0;ne[idx]=h[b];h[b]=idx++;
}
bool bfs(){
memset(d,-1,sizeof d);
d[S]=0;cur[S]=h[S];
queue<int>q;
q.push(S);
while(!q.empty()){
int t=q.front();
q.pop();
for(int i=h[t]; ~i; i=ne[i]){
int ver=e[i];
if(d[ver]==-1 && f[i]){
d[ver]=d[t]+1;
cur[ver]=h[ver];
if(ver==T)return true;
q.push(ver);
}
}
}
return false;
}
int find(int u, int limit){
if(u==T)return limit;
int flow=0;
for(int i=cur[u]; ~i && flow<limit; i=ne[i]){
int ver=e[i];
cur[u]=i;
if(d[ver]==d[u]+1 && f[i]){
int t=find(ver,min(f[i],limit-flow));
if(!t)d[ver]=-1;
f[i]-=t;f[i^1]+=t;flow+=t;
}
}
return flow;
}
int dinic(){
int res=0,flow=0;
while(bfs())while(flow=find(S,INF))res+=flow;
return res;
}
int main(){
cin>>n>>m;
S=0;T=n+1;
memset(h,-1,sizeof h);
for(int i=0; i<m; i++){
int a,b,c,d;
cin>>a>>b>>c>>d;
add(a,b,c,d);
s[a]-=c;s[b]+=c;//流入流出
}
int tot=0;
for(int i=1; i<=n; i++){
//流入大
//cout<<s[i]<<endl;
if(s[i]>0)add(S,i,0,s[i]),tot+=s[i];
else add(i,T,0,-s[i]);
}
// cout<<tot<<endl;
if(dinic()!=tot)puts("NO");
else{
puts("YES");
for(int i=0; i<2*m; i+=2){
cout<<f[i^1]+C[i]<<endl;
}
}
}
二.有源汇上下界最大流
题目:
我们首先考虑一个问题,无源汇上下界的最大流如何求?我们可以由题目一可以知道,我们构建的dinic最大流实际上是他的一个可行流(不一定是最大的),我们考虑那么一个事情,假设图中存在两个点s,t;我们可以知道我们是无法从s出发经过S或者T到达t的;也就是说当我们构建出可行流后,以s点开始跑dinic,一定不会影响到S和T的边权的,那么S和T的边权始终是满流状态(也就是说,我们从s点开始跑dinic,最终的图对应到原图还是一个可行流),于是我们可跑dinic,从而求出s->t的最大流,注意到s和t是循环的,所以我们需要断掉他们其中的一条边,并且记下边权res;
跑完所以最终的答案为res+dinic;
问题回到,有源汇上下界的最大流,我们考虑额外连接一条边t->s,边权为无穷大,那么我们就得到了一个无源汇上下界的最大流求解的题目(要求源点和汇点为s,t),于是我们可以直接套用上面的分析求解即可;
最重要的一点是:清楚S,T的边权在构建出来后就不会再改变(后面的最小流也是一样的道理)
代码如下:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N=210,M=(10210+N)*2,INF=1e9;
int n,m,S,T;
int h[N],ne[M],e[M],idx,f[M],C[M];
int s[N],d[N],cur[N];
void add(int a, int b, int c, int d){
e[idx]=b;f[idx]=d-c;C[idx]=c;ne[idx]=h[a];h[a]=idx++;
e[idx]=a;f[idx]=0;ne[idx]=h[b];h[b]=idx++;
}
bool bfs(){
memset(d,-1,sizeof d);
d[S]=0;cur[S]=h[S];
queue<int>q;
q.push(S);
while(!q.empty()){
int t=q.front();
q.pop();
for(int i=h[t]; ~i; i=ne[i]){
int ver=e[i];
if(d[ver]==-1 && f[i]){
d[ver]=d[t]+1;
cur[ver]=h[ver];
if(ver==T)return true;
q.push(ver);
}
}
}
return false;
}
int find(int u, int limit){
if(u==T)return limit;
int flow=0;
for(int i=cur[u]; ~i && flow<limit; i=ne[i]){
int ver=e[i];
cur[u]=i;
if(d[ver]==d[u]+1 && f[i]){
int t=find(ver,min(f[i],limit-flow));
if(!t)d[ver]=-1;
f[i]-=t;f[i^1]+=t;flow+=t;
}
}
return flow;
}
int dinic(){
int res=0,flow=0;
while(bfs())while(flow=find(S,INF))res+=flow;
return res;
}
int main(){
int ss,tt;
cin>>n>>m>>ss>>tt;
S=0;T=n+1;
memset(h,-1,sizeof h);
for(int i=0; i<m; i++){
int a,b,c,d;
cin>>a>>b>>c>>d;
add(a,b,c,d);
s[a]-=c;s[b]+=c;//流入流出
}
int tot=0;
for(int i=1; i<=n; i++){
//流入大
//cout<<s[i]<<endl;
if(s[i]>0)add(S,i,0,s[i]),tot+=s[i];
else add(i,T,0,-s[i]);
}
add(tt,ss,0,INF);
// cout<<tot<<endl;
if(dinic()!=tot)puts("No Solution");
else{
int res=f[idx-1];
f[idx-1]=f[idx-2]=0;
S=ss,T=tt;
cout<<res+dinic()<<endl;
}
}
三.有源汇上下界最小流
题目:
首先我们知道在跑完虚拟源汇点S,T的dinic后,再对其中的任何点跑dinic,得到的网络流依旧是一个可行流。不妨我们考虑,求s->t的最小流,首先我们知道初始流为res(S,T跑dinic得到的),我们考虑求出t->s的最大流,那么通过逆向,于是我们就得到了s->t的最小流为res-dinic(t->s);
代码如下:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N=50020,M=(125003+N)*2,INF=1e9;
int n,m,S,T;
int h[N],ne[M],e[M],idx,f[M],C[M];
int s[N],d[N],cur[N];
void add(int a, int b, int c, int d){
e[idx]=b;f[idx]=d-c;C[idx]=c;ne[idx]=h[a];h[a]=idx++;
e[idx]=a;f[idx]=0;ne[idx]=h[b];h[b]=idx++;
}
bool bfs(){
memset(d,-1,sizeof d);
d[S]=0;cur[S]=h[S];
queue<int>q;
q.push(S);
while(!q.empty()){
int t=q.front();
q.pop();
for(int i=h[t]; ~i; i=ne[i]){
int ver=e[i];
if(d[ver]==-1 && f[i]){
d[ver]=d[t]+1;
cur[ver]=h[ver];
if(ver==T)return true;
q.push(ver);
}
}
}
return false;
}
int find(int u, int limit){
if(u==T)return limit;
int flow=0;
for(int i=cur[u]; ~i && flow<limit; i=ne[i]){
int ver=e[i];
cur[u]=i;
if(d[ver]==d[u]+1 && f[i]){
int t=find(ver,min(f[i],limit-flow));
if(!t)d[ver]=-1;
f[i]-=t;f[i^1]+=t;flow+=t;
}
}
return flow;
}
int dinic(){
int res=0,flow=0;
while(bfs())while(flow=find(S,INF))res+=flow;
return res;
}
int main(){
int ss,tt;
cin>>n>>m>>ss>>tt;
S=0;T=n+1;
memset(h,-1,sizeof h);
for(int i=0; i<m; i++){
int a,b,c,d;
cin>>a>>b>>c>>d;
add(a,b,c,d);
s[a]-=c;s[b]+=c;//流入流出
}
int tot=0;
for(int i=1; i<=n; i++){
//流入大
//cout<<s[i]<<endl;
if(s[i]>0)add(S,i,0,s[i]),tot+=s[i];
else add(i,T,0,-s[i]);
}
add(tt,ss,0,INF);
if(dinic()!=tot)puts("No Solution");
else{
int res=f[idx-1];
f[idx-1]=f[idx-2]=0;
S=tt,T=ss;
cout<<res-dinic()<<endl;
}
}