最大流之上下界可行流
admin
2024-03-07 09:51:28

1.无源汇上下界可行流
没有源点和汇点,边的容量下界为low(u,v),上界为upp(u,v).求可行流.
我们只会求[0,c(u,v)]的最大流,一个直观的想法是所有边都减去下界。
但是这样是否能保证新图中满足容量限制和流量守恒么?
容量限制: 0<=f(u,v)-low(u,v)<=upp(u,v)-low(u,v)
流量限制: 会发现并不一定满足,因为每条边都减少low(u,v),可能使得入的多或者出的多。我们可以用源点和汇点协调一下,如果入的流量少于出的流量,可以从源点向对应点连边;反之亦然。
这时我们发现只要求出新图中的最大流,如果他是满流的,就对应了原图中的一个可行流。(因为满流说明源点流出的流量都流满了,原图中每个点流量守恒,符合可行流的定义)

#include
using namespace std;
typedef long long ll;
const int N = 500;
const int M = 2*(10900+N);
const int INF = 1e9;
int q[N];
int d[N];
int h[N],e[M],ne[M],w[M],idx = 0;
int cur[N];
int l[M]; //减去下界后记得加上 
int n,m,k,T; int st,ed;
int in[N]; //减少的入的流量 
int out[N]; //减少的出的流量
ll tot = 0;
void add(int a,int b,int c)
{e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
bool bfs()
{int hh = 0,tt = 0;memset(d,-1,sizeof(d));d[st] = 0,cur[st] = h[st],q[tt++] = st;while(hh!=tt){int u = q[hh++];for(int i=h[u];~i;i=ne[i]){int j = e[i];if(d[j]==-1&&w[i]){cur[j] = h[j];d[j] = d[u]+1;if(j==ed) return 1;q[tt++] = j;}}}return 0;
}
ll find(int u,ll limit)
{if(u==ed) return limit;ll flow = 0;for(int i=cur[u];~i&&flowcur[u] = i;int j = e[i];if(d[j]==d[u]+1&&w[i]){ll t = find(j,min(1ll*w[i],limit-flow));if(!t) d[j] = -1;flow += t,w[i] -= t,w[i^1] += t;}}return flow;
}
ll dinic()
{ll ans = 0,flow;while(bfs()) while(flow = find(st,INF)) ans += flow;return ans;
}
void solve()
{memset(h,-1,sizeof(h));scanf("%d%d",&n,&m);st = 0,ed = n+1;for(int i=1;i<=m;++i){int a,b,c,d;scanf("%d%d%d%d",&a,&b,&c,&d);l[idx] = c;add(a,b,d-c);add(b,a,0);out[a] += c;in[b] += c;}for(int i=1;i<=n;++i){int wh = abs(in[i]-out[i]);if(in[i]>out[i]){add(st,i,wh),add(i,st,0);}else if(in[i]add(i,ed,wh),add(ed,i,0);}}ll tot = 0;for(int i=h[st];~i;i=ne[i]){tot += w[i];    }   ll ans = dinic();if(ans != tot){printf("NO");}else{printf("YES\n");for(int i=0;i<2*m;i+=2){printf("%d\n",w[i^1]+l[i]); //对于可行流f,残留网络里的边权对应可行流的边的流量,原图中的边权对应容量-流量 }}
}
signed main(void)
{solve();return 0;
} 

2.有源汇上下界最大流
有源点和汇点、上下界。
可以按照无源汇的方法建图,此时除了源点和汇点都满足流量守恒,此时从汇点向源点连一条正无穷的边,则所有点满足流量守恒。若求可行流,问题就转化为无源汇的可行流。
但是我们要求比较高,求最大流。
一个直观的想法是求超级源点到超级汇点的最大流,之后再求原图中源点到汇点的最大流(因为求完第一个最大流之后可能还存在原图中源点到汇点的增广路)。其实这个想法不严谨,但是可以证明是正确的。因为求出超级源点到超级汇点的最大流(满流)之后,他们对应的边就算废了,因为都是满流,相当于没有。这个时候把正无穷的边删去,求得的原图中源点到原图中汇点的最大流满足可行流的定义(流量限制和容量限制)。

#include
using namespace std;
typedef long long ll;
const int N = 210;
const int M = 3*(10000+N);
const int INF = 2e9;
int q[N]; int d[N]; int cur[N];
int h[N],e[M],ne[M],w[M],idx = 0;
int in[N],out[N];
int st,ed,st2,ed2;
int n,m,k,T;
void add(int a,int b,int c)
{e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
bool bfs()
{int hh = 0,tt = 0;memset(d,-1,sizeof(d));d[st] = 0,cur[st] = h[st],q[tt++] = st;while(hh!=tt){int u = q[hh++];for(int i=h[u];~i;i=ne[i]){int j = e[i];if(d[j]==-1&&w[i]){d[j] = d[u] + 1;cur[j] = h[j];if(j==ed) return true;q[tt++] = j;}}}return 0;
}
ll find(int u,ll limit)
{if(u==ed) return limit;ll flow = 0;for(int i=cur[u];~i&&flowcur[u] = i;int j = e[i];if(d[j]==d[u]+1&&w[i]){ll t = find(j,min(1ll*w[i],limit-flow));if(!t) d[j] = -1;flow += t; w[i] -= t,w[i^1] += t;}}return flow;
}
ll dinic()
{ll ans = 0,flow;while(bfs()) while(flow = find(st,INF)) ans += flow;return ans;
}
void solve()
{memset(h,-1,sizeof(h));scanf("%d%d%d%d",&n,&m,&st2,&ed2);st = 0,ed = n+1;while(m--){int a,b,c,d; scanf("%d%d%d%d",&a,&b,&c,&d);add(a,b,d-c);add(b,a,0);out[a]+=c;in[b]+=c;}for(int i=1;i<=n;++i){int wh = abs(in[i]-out[i]);if(in[i]>out[i]){add(st,i,wh);add(i,st,0);}else if(in[i]add(i,ed,wh);add(ed,i,0);}}add(ed2,st2,INF); //流量为无穷的边,流量守恒 add(st2,ed2,0);ll tot = 0;for(int i=h[st];~i;i=ne[i]) tot += w[i]; ll ans = dinic();if(ans < tot){printf("No Solution");}else{int res = w[idx-1];w[idx-1] = w[idx-2] = 0;st = st2,ed = ed2;printf("%lld",dinic() + res);}
}
signed main(void)
{solve();return 0;
}

3.有源汇上下界最小流
基本思想同理最大流。先求出满流,保证是个可行流,然后再求原图中汇点到源点的最大流,反着流最大即正着流最小。

#include
using namespace std;
typedef long long ll;
const int N = 5e4+10;
const int M = 2*(125003+N);
const ll INF = 1e18;
int q[N]; int d[N]; int cur[N];
int h[N],e[M],ne[M];
ll w[M];
int idx = 0;
int in[N],out[N];
int st,ed,st2,ed2;
int n,m,k,T;
void add(int a,int b,ll c)
{e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
bool bfs()
{int hh = 0,tt = 0;memset(d,-1,sizeof(d));d[st] = 0,cur[st] = h[st],q[tt++] = st;while(hh!=tt){int u = q[hh++];for(int i=h[u];~i;i=ne[i]){int j = e[i];if(d[j]==-1&&w[i]){d[j] = d[u] + 1;cur[j] = h[j];if(j==ed) return true;q[tt++] = j;}}}return 0;
}
ll find(int u,ll limit)
{if(u==ed) return limit;ll flow = 0;for(int i=cur[u];~i&&flowcur[u] = i;int j = e[i];if(d[j]==d[u]+1&&w[i]){ll t = find(j,min(1ll*w[i],limit-flow));if(!t) d[j] = -1;flow += t; w[i] -= t,w[i^1] += t;}}return flow;
}
ll dinic()
{ll ans = 0,flow;while(bfs()) while(flow = find(st,INF)) ans += flow;return ans;
}
void solve()
{memset(h,-1,sizeof(h));scanf("%d%d%d%d",&n,&m,&st2,&ed2);st = 0,ed = n+1;while(m--){int a,b,c,d; scanf("%d%d%d%d",&a,&b,&c,&d);add(a,b,d-c);add(b,a,0);out[a]+=c;in[b]+=c;}for(int i=1;i<=n;++i){int wh = abs(in[i]-out[i]);if(in[i]>out[i]){add(st,i,wh);add(i,st,0);}else if(in[i]add(i,ed,wh);add(ed,i,0);}}add(ed2,st2,INF); //流量为无穷的边,流量守恒 add(st2,ed2,0); ll tot = 0;for(int i=h[st];~i;i=ne[i]) tot += w[i]; ll ans = dinic();if(ans < tot){printf("No Solution");}else{int res = w[idx-1];w[idx-1] = w[idx-2] = 0;st = ed2,ed = st2;printf("%lld",res-dinic());}
}
signed main(void)
{solve();return 0;
}
/*
3 2 1 3
1 2 1000 2000
2 3 100 200
*/

相关内容

热门资讯

从“行业自律”到政策倒逼,大全... 在2025年经历了连续6个月的股价拉升后,大全新能源(DQ.US)最近3个月股价回落明显。从去年11...
《海洋生物多样性协定》生效,全... 格隆汇1月17日|据央视新闻,全球首部具有法律约束力的公海生物多样性保护国际公约——《海洋生物多样性...
为什么上完厕所体重没变轻?有时... 很多人都喜欢在早上吃饭前称体重,毕竟这时候的体重最轻,称起来更有“成就感”。有些“细心”的人还会在拉...
县政府学雷锋志愿服务活动倡议书...  广大志愿者朋友们、市民朋友们:  今年是毛泽东等老一辈无产阶级革命家发出“向雷锋同志学习”伟大号召...