最小费用流模板(zkw与spfa)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ike940067893/article/details/82859821

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

扫描二维码关注公众号,回复: 3715105 查看本文章

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

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

或者是增广路径比较短的图 (如二分图), 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);
}

猜你喜欢

转载自blog.csdn.net/Ike940067893/article/details/82859821