“zkw” 费用流算法在哪些图上慢(摘自https://www.cnblogs.com/ECJTUACM-873284962/p/7744943.html

实践中, 上面的这个算法非常奇怪. 在某一些图上, 算法速度非常快,

另一些图上却比纯 SPFA 增广的算法慢. 不少同学经过实测总结的结果是稠密图上比较快,

稀疏图上比较慢, 但也不尽然. 这里我从理论上分析一下, 究竟这个算法用于哪些图可以得到理想的效果.

先分析算法的增广流程. 和 SPFA 直接算法相比, 由于同属于沿最短路增广的算法,

实际进行的增流操作并没有太多的区别, 每次的增流路径也大同小异. 因此不考虑多路增广时,

增广次数应该基本相同. 运行时间上主要的差异应当在于如何寻找增流路径的部分.

那么 zkw 算法的优势在于哪里呢?

与 SPFA 相比, KM 的重标号方式明显在速度上占优, 每次只是一个对边的扫描操作而已.

而 SPFA 需要维护较为复杂的标号和队列操作, 同时为了修正标号, 需要不止一次地访问某些节点, 速度会慢不少.

另外, 在 zkw 算法中, 增广是多路进行的, 同时可能在一次重标号后进行多次增广.

这个特点可以在许多路径都费用相同的时候派上用场, 进一步减少了重标号的时间耗费.

下面想一想 zkw 算法的劣势, 也就是 KM 重标号方式存在的问题.

KM 重标号的主要问题就是, 不保证经过一次重标号之后能够存在增广路.

最差情况下, 一次只能在零权网络中增加一条边而已. 这时算法就会反复重标号,

反复尝试增广而次次不能增广, 陷入弄巧成拙的境地.

接下来要说什么, 大家可能已经猜到了. 对于最终流量较大, 而费用取值范围不大的图,

或者是增广路径比较短的图 (如二分图), zkw 算法都会比较快. 原因是充分发挥优势.

比如流多说明可以同一费用反复增广, 费用窄说明不用改太多距离标号就会有新增广路,

增广路径短可以显著改善最坏情况, 因为即使每次就只增加一条边也可以很快凑成最短路.

如果恰恰相反, 流量不大, 费用不小, 增广路还较长, 就不适合 zkw 算法了.

费用流板题:luoguP3381 【模板】最小费用最大流

zkw版/板:

#include <bits/stdc++.h>
using namespace std;
void read(int &num)
{
char ch; bool flag=0;
while(!isdigit(ch=getchar()))if(ch=='-')flag=!flag;
for(num=0;isdigit(ch);num=num*10+ch-'0',ch=getchar());
if(flag)num=-num;
}
const int MAXN = 5005;
const int MAXM = 50005;
const int inf = 1e9;
int n, m, S, T, Ans;
int cnt = 1, fir[MAXN], nxt[MAXM*2], to[MAXM*2], c[MAXM*2], w[MAXM*2];
int dis[MAXN];
bool vis[MAXN]; void Add(int u, int v, int cc, int wt)
{
to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; c[cnt] = cc; w[cnt] = wt;
} int Aug(int u, int flow)
{
if(u == T) { return flow; }
int used = 0, delta = 0;
vis[u] = true;
for(int i = fir[u]; i; i = nxt[i])
if(!vis[to[i]] && c[i] && dis[u] == dis[to[i]] + w[i])
{
delta = Aug(to[i], min(c[i], flow-used));
c[i] -= delta, c[i^1] += delta, used += delta;
Ans += delta * w[i];
if(used == flow) break;
}
return used;
} bool Update()
{
int tmp = inf;
for(int i = 1; i <= n; i++) if(vis[i])
for(int j = fir[i]; j; j = nxt[j])
if(!vis[to[j]] && c[j]) tmp = min(tmp, dis[to[j]]-dis[i]+w[j]);
if(tmp == inf) return false;
for(int i = 1; i <= n; i++) if(vis[i]) dis[i] += tmp;
return true;
} int Cost_flow()
{
int flow = 0, tmp = 0;
do {
do { flow += tmp; memset(vis, 0, sizeof vis); }while(tmp=Aug(S, inf));
}while(Update());
return flow;
} int main ()
{
int u, v, x, y;
read(n), read(m), read(S), read(T);
for(int i = 1; i <= m; i++)
{
read(u), read(v), read(x), read(y);
Add(u, v, x, y);
Add(v, u, 0, -y);
}
int Max_flow = Cost_flow();
printf("%d %d\n", Max_flow, Ans);
}

spfa版:

#include <bits/stdc++.h>
using namespace std;
void read(int &num)
{
char ch; bool flag=0;
while(!isdigit(ch=getchar()))if(ch=='-')flag=!flag;
for(num=0;isdigit(ch);num=num*10+ch-'0',ch=getchar());
if(flag)num=-num;
}
const int MAXN = 5005;
const int MAXM = 50005;
const int inf = 1e9;
int n, m, S, T, Ans;
int cnt = 1, fir[MAXN], nxt[MAXM*2], to[MAXM*2], c[MAXM*2], w[MAXM*2];
int dis[MAXN];
bool vis[MAXN]; void Add(int u, int v, int cc, int wt)
{
to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; c[cnt] = cc; w[cnt] = wt;
} bool spfa(int s, int t)
{
memset(vis, 0, sizeof vis);
for(int i = 0; i <= n; i++) dis[i] = inf;
dis[t] = 0; vis[t] = true;
deque<int> Q; Q.push_back(t);
while(!Q.empty())
{
int u = Q.front(); Q.pop_front();
for(int i = fir[u]; i; i = nxt[i])
if(c[i^1] && dis[to[i]] > dis[u] - w[i])
{
dis[to[i]] = dis[u] - w[i];
if(!vis[to[i]])
{
vis[to[i]] = true;
if(!Q.empty() && dis[to[i]] < dis[Q.front()]) Q.push_front(to[i]);
else Q.push_back(to[i]);
}
}
vis[u] = 0;
}
return dis[S] < inf;
} int Aug(int u, int flow)
{
if(u == T) { vis[T] = true; return flow; }
int used = 0, delta = 0;
vis[u] = true;
for(int i = fir[u]; i; i = nxt[i])
if(!vis[to[i]] && c[i] && dis[u] == dis[to[i]] + w[i])
{
delta = Aug(to[i], min(c[i], flow-used));
Ans += delta * w[i], c[i] -= delta, c[i^1] += delta, used += delta;
if(used == flow) break;
}
return used;
} int Cost_flow()
{
int flow = 0;
while(spfa(S, T))
{
vis[T] = 1;
while(vis[T])
{
memset(vis, 0, sizeof vis);
flow += Aug(S, inf);
}
}
return flow;
} int main ()
{
int u, v, x, y;
read(n), read(m), read(S), read(T);
for(int i = 1; i <= m; i++)
{
read(u), read(v), read(x), read(y);
Add(u, v, x, y);
Add(v, u, 0, -y);
}
int Max_flow = Cost_flow();
printf("%d %d\n", Max_flow, Ans);
}

最新文章

  1. 路由信息协议(RIP)的防环机制
  2. MongoDB 备份(mongodump)恢复(mongorerstore) 导出 (Mongoexport) 导入( Mongoimport)
  3. 基于OWIN WebAPI 使用OAUTH2授权服务【授权码模式(Authorization Code)】
  4. Device.js – 快速检测平台、操作系统和方向信息
  5. 【C#进阶系列】01 CLR的执行模型——一个Hello World的故事
  6. Python安装指南
  7. mysql 使用说明-2
  8. Form实现主从块金额汇总
  9. Swift2.0 中的String(一):常用属性
  10. highcharts设置Area颜色透明度
  11. apache常见错误汇总
  12. Android(java)学习笔记99:android的短信发送器研究
  13. ssh-add命令
  14. uva12589
  15. CSS概念,引入,选择器
  16. 编程菜鸟的日记-初学尝试编程-C++ Primer Plus 第5章编程练习7
  17. 富文本编辑器Quill(一)简单介绍
  18. app常见专项测试点
  19. Aspose.Cells.dll的用法
  20. weblogic上部署项目出错

热门文章

  1. SpringBoot配置文件敏感信息加密-jasypt
  2. 2.Jvm 虚拟机栈和栈帧
  3. Python-12-装饰器
  4. 性能监控工具的配置及使用 - Spotlight On Oracle(oracle) 转:紫漪
  5. 【SQL Server数据迁移】32位的机器:SQL Server中查询ORACLE的数据
  6. C# HtmlAgilityPack爬取静态页面
  7. Python接口自动化基础---环境准备
  8. TCP 为什么需要三次握手而不是两次
  9. Office 365 的安装与激活
  10. JS权威指南读书笔记(七)