流量网络
• 想要将一些水从S运到T,必须经过一些水站,链接水站的是管道,每条管道都有它的最大能容纳的水量,求最多S到T能流多少流量。

基本概念
• 这是一个典型的网络流模型。我们先了解网络流的有关定义和概念。
• 若有向图G=(V,E)满足下列条件:

  1. 有且仅有一个顶点S,它的入度为零,即d-(S) = 0,这个顶点S便称为源点,或称为发点。
  2. 有且仅有一个顶点T,它的出度为零,即d+(T) = 0,这个顶点T便称为汇点,或称为收点。
  3. 每一条弧都有非负数,叫做该边的容量。边(vi, vj)的容量用cij表示。
    • 则称之为网络流图,记为G = (V, E, C)

    1 .水流不递增
    2 .不能存水
    3 流入多少水流出多少水

可改进路(增广路)

残留网络

割切


找一个方法,把S和T断开,所切截面为流量
所能通过的水流为截面,
所以最大流等于最小割

流量算法的基本理论

• 定理1:对于已知的网络流图,设任意一可行流为f,任意一割切为(U, W),必有:V(f) ≤ C(U, W)。
• 定理2:可行流f是最大流的充分必要条件是:f的残量网络中不存在可改进路。
• 定理3:整流定理——
如果网络中所有的弧的容量是整数,则存在整数值的最大流。
• 定理4:最大流最小割定理——
最大流等于最小割,即max V(f) = min C(U, W)。

方法:

Ford-Fulkson方法
FF方法

Edmond-Karp算法

• EK算法是FF方法中最简单的一个,当然效率也就比较低
• EK算法的流程与ff方法完全相同,只是在寻找增广路的时候使用了BFS
• 时间复杂度O(n*m^2)
• 空间复杂度O(n^2)(邻接矩阵)
• 注意要退流哦!!!!
复杂度太高基本不用

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=205;
const int inf=0x7fffffff;
int r[maxn][maxn]; //残留网络,初始化为原图
bool visit[maxn];
int pre[maxn];
int m,n;
bool bfs(int s,int t)  //寻找一条从s到t的增广路,若找到返回true
{int p;queue<int > q;memset(pre,-1,sizeof(pre));memset(visit,false,sizeof(visit));pre[s]=s;visit[s]=true;q.push(s);while(!q.empty()){p=q.front();q.pop();for(int i=1;i<=n;i++){if(r[p][i]>0&&!visit[i]){pre[i]=p;visit[i]=true;if(i==t) return true;q.push(i);}}}return false;
}
int EdmondsKarp(int s,int t)
{int flow=0,d,i;while(bfs(s,t)){d=inf;for(i=t;i!=s;i=pre[i])d=d<r[pre[i]][i]? d:r[pre[i]][i];for(i=t;i!=s;i=pre[i]){r[pre[i]][i]-=d;r[i][pre[i]]+=d;}flow+=d;}return flow;
}int main()
{while(scanf("%d%d",&m,&n)!=EOF){int u,v,w;memset(r,0,sizeof(r));///for(int i=0;i<m;i++){scanf("%d%d%d",&u,&v,&w);r[u][v]+=w;}printf("%d\n",EdmondsKarp(1,n));}return 0;
}

Sap算法

• sap算法是采用了距离标号的方法求最短增广路, 而距离标号的思想来自于push__relable(压入重标记)类算法
• 有兴趣的同学可以去了解一下push__relable类算法

int dfs(int x,int flow)
{if(x==t)return flow;//如果是汇点,从x流到t的流量 int sum=0;//当前情况下x流出多少流量 for(int i=1;i<=m;i++) {if(g[x][i]&&(d[x]==d[i]+1))//g[x][i]:x可以流到i {int tmp=dfs(i,min(g[x][i],flow-sum));//受限于x到i的边和剩余流量 g[x][i]-=tmp; g[i][x]+=tmp;//反向边 sum+=tmp;//流出tmp流量 if(sum==flow)return sum;//如果所有流量用完了 //如果流量没有用完,继续循环 }}if(d[S]==-1)return sum;//出现断层,已经断开 cntd[d[x]]--;//cntd[i]第i层有多少点   d[x]层的点 if(!cntd[d[x]])//这层没点了 ,说明出现断层 d[S]=-1;    //S最多在第m-1层,当出现在第-1层说明为不合理情况,要退出 ,相当于退出标记 d[x]++;//上升一层 cntd[d[x]]++;//层数点加1 return sum;
}dfs(S,inf);

Dinic算法

• Dinic算法是一个基于“层次图”的时间效率优先的最大流算法。
• 层次图是什么东西呢?层次,其实就是从源点走到那个点的最短路径长度。
• 于是乎,我们得到一个定理:从源点开始,在层次图中沿着边不管怎么走,经过的路径一定
是终点在剩余图中的最短路。

• Dinic算法也有类似于sap的为顶点定标的过程——通常用BFS实现……
• 每次从源点开始遍历,如果一次bfs可以从源点到汇点整个网络都会变成一个层次网络
• 在一个分层网络中, 只有a[i] = = a[j] or a[i] = = a[j] -1时<i,j>才有边存在。
• 当且仅当a[i] == a[j] -1时,两点之间的边才被称为有用边,在接下来的寻找增广路的过程中只会走这样的有用边。
• 因此,dfs的时候只要遍历到汇点即可因为同一层和下一层都更新不了汇点了


bool makelevel(int s,int t)
{memset(d,0,sizeof(d));memset(q,0,sizeof(q));d[s]=1;//s的层级为1int l=0,r=0;q[r++]=s;while(l<r){int x=q[l++];if(x==t)return 1;//到汇点时退出for(int i=head[x];i;i=edge[i].next){if((d[edge[i].t]==0)&&(edge[i].w!=0))//容量为0的边不能走&&该点的层级未被标记{q[r++]=edge[i].t;d[edge[i].t]=d[x]+1;//层级加一}}}return false;
}
int dfs(int x,int flow,int t)//从s到x流了多少流量
{if(x==t)return flow;int sum=0;for(int i=head[x];i;i=edge[i].next){if((edge[i].w!=0)&&(d[edge[i].t]==d[x]+1))//满足层级要求{int tmp=dfs(edge[i].t,min(flow-sum,edge[i].w),t);edge[i].w-=tmp;edge[i^1].w+=tmp;sum+=tmp;if(sum==flow)return sum;}}return sum;
}
while(makelevel(1,m))
{ans+=dfs(1,INF,m);
}

完整代码:

#include <bits/stdc++.h>
using namespace std;
const long long inf=2005020600;
int n,m,s,t,u,v;
long long w,ans,dis[520010];
int tot=1,now[520010],head[520010]; struct node {int to,net;long long val;
} e[520010];inline void add(int u,int v,long long w) {e[++tot].to=v;e[tot].val=w;e[tot].net=head[u];head[u]=tot;e[++tot].to=u;e[tot].val=0;e[tot].net=head[v];head[v]=tot;
}inline int bfs() {  //在惨量网络中构造分层图 for(register int i=1;i<=n;i++) dis[i]=inf;queue<int> q;q.push(s);dis[s]=0;now[s]=head[s];while(!q.empty()) {int x=q.front();q.pop();for(register int i=head[x];i;i=e[i].net) {int v=e[i].to;if(e[i].val>0&&dis[v]==inf) {q.push(v);now[v]=head[v];dis[v]=dis[x]+1;if(v==t) return 1;}}}return 0;
}inline int dfs(int x,long long sum) {  //sum是整条增广路对最大流的贡献if(x==t) return sum;long long k,res=0;  //k是当前最小的剩余容量 for(register int i=now[x];i&&sum;i=e[i].net) {now[x]=i;  //当前弧优化 int v=e[i].to;if(e[i].val>0&&(dis[v]==dis[x]+1)) {k=dfs(v,min(sum,e[i].val));if(k==0) dis[v]=inf;  //剪枝,去掉增广完毕的点 e[i].val-=k;e[i^1].val+=k;res+=k;  //res表示经过该点的所有流量和(相当于流出的总量) sum-=k;  //sum表示经过该点的剩余流量 }}return res;
}int main() {scanf("%d%d%d%d",&n,&m,&s,&t);for(register int i=1;i<=m;i++) {scanf("%d%d%lld",&u,&v,&w);add(u,v,w);}while(bfs()) {ans+=dfs(s,inf);  //流量守恒(流入=流出) }printf("%lld",ans);return 0;
}
//dinic时间复杂度:O(n^2 m).

最小费用最大流

• 带有费用的网络流图: G=(V,E,C,W) V:顶点; E:弧;C:弧的容量;W:单位流量费用。
保证s到t流量最大的前提下,所需的费用最小,这就是最小费用最大流问题.

基本思路:

把弧<i,j>的单位费用w[i,j]看作弧<i,j>的路径长度,每次找从源点s到汇点t长度最短(费用最小)的可增广路径进行增广。

  1. 最小费用可增广路
  2. 路径s到t的长度即单位流量的费用。
    • 其实就是把ek算法中的广搜改成spfa……(因为存在负权边所以得用spfa)

代码:

bool spfa(int s,int t)
{queue<int>q;for(int i=0;i<N;i++){dis[i]=INF;vis[i]=0;pre[i]=-1;}dis[s]=0;vis[s]=1;q.push(s);while(!q.empty()){int u=q.front();q.pop();vis[u]=1;for(int i=head[u];i;i=edge[i].next){int v=edge[i].to;if(edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost){dis[v]=dis[u]+edge[i].cost;pre[v]=i;if(!vis[v]){vis[v]=1;q.push(v);}}}}if(pre[t]==-1)return 0;return 1;
}
int minCostMaxflow(int s,int t,int &cost)
{int flow=0;cost=0;while(spfa(s,t)){int Min=INF;for(int i=pre[t];i;i=pre[edge[i^1].to]){if(Min>edge[i].cap-edge[i].flow)Min=edge[i].cap-edge[i].flow;}for(int i=pre[t];i;i=pre[edge[i^1].to]){edge[i].flow+=Min;edge[i^1].flow-=Min;cost+=edge[i].cost;edge[i^1].flow+=edge[i].cost;edge[i].cost-=edge[i].cost;}flow+=Min;}return flow;
}
//返回的最大流,cost存的是最小费用

洛谷模板题

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define ll long long
#define inf 50000000
#define re register
using namespace std;
struct po
{int from,to,dis,nxt,w;
}edge[250001];
int head[250001],cur[1000001],dep[60001],n,m,s,t,u,num=-1,x,y,l,tot,sum,k,fa[10001];
int dis[5001],vis[5001],xb[5001],flow[5001];
inline int read()
{int x=0,c=1;char ch=' ';while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();while(ch=='-')c*=-1,ch=getchar();while(ch<='9'&&ch>='0')x=x*10+ch-'0',ch=getchar();return x*c;
}
inline void add_edge(int from,int to,int w,int dis)
{edge[++num].nxt=head[from];edge[num].from=from;edge[num].to=to;edge[num].w=w;edge[num].dis=dis;head[from]=num;
}
inline void add(int from,int to,int w,int dis)
{add_edge(from,to,w,dis);add_edge(to,from,0,-dis);
}
inline bool spfa()
{memset(dis,100,sizeof(dis));memset(vis,0,sizeof(vis));queue<int> q;while(!q.empty())q.pop();for(re int i=1;i<=n;i++){fa[i]=-1;}vis[s]=1;dis[s]=0;fa[s]=0;flow[s]=inf;q.push(s);while(!q.empty()){int u=q.front();q.pop();vis[u]=0;for(re int i=head[u];i!=-1;i=edge[i].nxt){int v=edge[i].to;if(edge[i].w>0&&dis[v]>dis[u]+edge[i].dis){dis[v]=dis[u]+edge[i].dis;fa[v]=u;xb[v]=i;flow[v]=min(flow[u],edge[i].w);if(!vis[v]){vis[v]=1,q.push(v);}}}}return dis[t]<inf;
}
inline void max_flow()
{while(spfa()){int k=t;while(k!=s){edge[xb[k]].w-=flow[t];edge[xb[k]^1].w+=flow[t];k=fa[k];}tot+=flow[t];sum+=flow[t]*dis[t];}
}
int main()
{memset(head,-1,sizeof(head));int d;n=read();m=read();s=read();t=read();for(re int i=1;i<=m;i++){x=read();y=read();l=read();d=read();add(x,y,l,d);}max_flow();cout<<tot<<" "<<sum;
}

网络流专题(最大流与费用流)(一)相关推荐

  1. 餐巾计划问题 线性规划与网络流24题之10 费用流

    相关知识:最小费用(最大)流 问题描述: 一个餐厅在相继的N 天里, 每天需用的餐巾数不尽相同. 假设第i天需要ri块餐巾(i=1, 2,-,N).餐厅可以购买新的餐巾,每块餐巾的费用为p分:或者把旧 ...

  2. [模版] 网络流最大流、费用流

    反向边作用讨论:http://blog.csdn.net/qq_21110267/article/details/43540483 我理解的很有限,希望有研究过的人能给我评论指导. 代码: By:Ru ...

  3. 图论 —— 网络流 —— 费用流 —— zkw 费用流

    [概述] 求解费用流的方法很多,目前最流行的是 MCMF 费用流,其实质是将 EK 算法中的 bfs 换为了 SPFA 来计算最小费用,但其存在的一个缺点是 EK 是单路增广的,这样速度会相应的慢一些 ...

  4. zkw费用流 java_[模板] 网络流相关/最大流ISAP/费用流zkw

    最大流/ISAP 话说ISAP是真快...(大多数情况)吊打dinic,而且还好写... 大概思路就是: 在dinic的基础上, 动态修改层数, 如果终点层数 $>$ 点数, break. 暂时 ...

  5. 【模板】最小费用最大流【费用流】

    题目大意: 给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用. 思路: 既然是模板题,那么数据肯定很水. 我 E K + S P F A ...

  6. 网络流初步——最大流费用流

    网络流初步--最大流&最小割&费用流 一.基础定义 网络 :一个网络G=(V,E)是一张有向图: 容量 :图中每条边(u,v)∈E都有一个给定的权值c(u,v)被称为容量 源/汇点:两 ...

  7. 图论-网络流⑦-费用流解题

    图论-网络流⑦-费用流解题 上一篇:图论-网络流⑥-费用流 下一篇:图论-网络流⑧-有上下界的网络流 参考文献: https://www.luogu.com.cn/blog/user9012/solu ...

  8. 【图论】【网络流】费用流模型

    费用流模型 费用流板子 费用流直接应用 运输问题 负载平衡问题 二分图最大匹配 分配问题 最大权不相交路径 数字梯形问题 费用流网格图模型 K取方格数 深海机器人问题 费用流拆点 餐巾计划问题 费用流 ...

  9. [BZOJ 1221][HNOI2001]软件开发(费用流)

    Description 某软件公司正在规划一项n天的软件开发计划,根据开发计划第i天需要ni个软件开发人员,为了提高软件开发人员的效率,公司给软件人员提供了很多的服务,其中一项服务就是要为每个开发人员 ...

最新文章

  1. SRX 透明模式配置
  2. 你不会编程,不是你不行,很有可能是老师教的方法不好。科学家发现:对大脑而言,代码编程与语言学习不同...
  3. python turtle库画图案-python之绘制图形库turtle(转)
  4. 自定义组件 点击空白处隐藏
  5. 通过源码告诉你,阿里的RocketMQ事务消息到底牛逼在哪?
  6. 华为k662c的虚拟服务器,华为k662c路由器怎么设置
  7. Java技术学习心得
  8. java-log入门【目的把日志写入socket】
  9. keil复制代码乱码_成都控制器开发:容易忽略!用KEIL编码汉字也会有BUG
  10. MyCat分布式数据库集群架构工作笔记0016---高可用_单表存储千万级_海量存储_垂直分库划分原则
  11. 阿里云官方推出操作系统“等保合规”镜像 -- Alibaba Cloud Linux 等保2.0三级版
  12. 一套价值800元的爱代挂源码完整版
  13. 更新显卡驱动后,Windows重启卡在Logo页面
  14. CNCC|AI大模型开源开放生态技术论坛,助力多领域AI生态发展
  15. 【Android 教程系列第 27 篇】如何使用 Keytool 工具生成 keystore 签名文件
  16. 信杂比公式_关于信噪比SNR【转载】
  17. Java web 服务器 搭建_搭建一个java web服务端
  18. 指定图片指定方式加密
  19. ABAP smartforms 打印多页打印在同一页上解决
  20. java dozer_java Dozer深copy工具类

热门文章

  1. 相亲有风险,且行且珍惜!| 今日最佳
  2. 前端又一本升级版图书上市了,听说比第一版还好看
  3. python join_python join 和 split的常用使用方法
  4. mysql xa 演示_mysql的XA事务恢复过程详解
  5. kafka 怎么样连接图形化界面_图形化编程有多简单,点亮LED不到一分钟
  6. Android 封装handler,android封装工作线程跟Handler工具类
  7. 扩展mysql_扩展mysql - 手把手教你写udf
  8. librosa能量_语音MFCC提取:librosa amp;amp; python_speech_feature(2019.12)
  9. 古登堡是垂直搜索引擎吗_网站排名,提高内容输出频率,就一定要对排名好吗?-SEO...
  10. sqlsession.selectlist 会返回null么_StackOverflow经典问题:代码中如何去掉烦人的“!=nullquot;判空语句...