zkw费用流

zkw费用流学习笔记

根据OI业界潜规则,出网络流题目时候,最大流不能卡 dinic ,费用流不能卡 ek ,但是我今天做了一道 [SDOI2009]最优图像 把我惊到了,反正就是把费用流的 ek 算法给卡了,所以今天还要学习一下这个 zkw费用流

说到 zkw费用流,我就想起今年上半年NOIWC2019上laofu讲到的模拟费用流问题

算法流程:每次先dfs一遍(和dinic的dfs一样)dfs增广,用一个vis数组记录下增广过程中经过了哪些点,然后再xjb维护一下每个点的顶标,下面代码观察的angle_kitty巨佬的,维护顶标的做法是直接把顶标扔费用里了(可能比较好些但是不是很好理解反正我没理解背过就行

维护顶标就是找所有增广过点连出的流量中花费最小的一个,让正向边花费减去它反向边花费加上它,还要维护一个全全局变量(可能是代表从src到dest默认已经付的路费吧

代码不长,和dinic差不多,性能比EK好

另外在最优图像那道题中由于没有SPJ,这么写会WA,期望你谷早日配上SPJ(我只会写lemon风格的SPJ,testlib的不会写) BZ和loj都没那道题。。

一下为你谷费用流模板代码

#include <cstdio>
#include <cstring>
using namespace std;

struct edge { int v, ne, flow; int cost; } a[1000010];
int h[12306], n, m, tmp = 1, src = 1, dest = 2, tot = 2, maxflow;
int dis[12306], now[12306], mincost, co;
bool vis[12306];

template <class _Tp> void chkmin(_Tp &a, _Tp b) { if (a > b) a = b; }
void add(int x, int y, int flow, int cost) { a[++tmp] = (edge){y, h[x], flow, cost}; h[x] = tmp; }
void add1(int x, int y, int flow, int cost) { add(x, y, flow, cost), add(y, x, 0, -cost); }

int aug(int x, int want)
{
	if (x == dest) { mincost += co * want, maxflow += want; return want; }
	vis[x] = true;
	int get = 0;
	for (int i = now[x]; i != 0; i = a[i].ne)
	{
		if (a[i].flow > 0 && a[i].cost == 0 && vis[a[i].v] == false)
		{
			int f = aug(a[i].v, min(a[i].flow, want));
			a[i].flow -= f, a[i ^ 1].flow += f;
			want -= f, get += f;
		}
		now[x] = i;
		if (want == 0) break;
	}
	return get;
}

bool modlabel()
{
	int tmp = 1e9;
	for (int x = 1; x <= tot; x++) if (vis[x])
		for (int i = h[x]; i != 0; i = a[i].ne)
			if (a[i].flow > 0 && vis[a[i].v] == false)
				chkmin(tmp, a[i].cost);
	if (tmp == 1e9) return false;
	for (int x = 1; x <= tot; x++) if (vis[x])
		for (int i = h[x]; i != 0; i = a[i].ne)
			a[i].cost -= tmp, a[i ^ 1].cost += tmp;
	co += tmp;
	return true;
}

void zkw()
{
	do	do	for (int i = 1; i <= tot; i++) now[i] = h[i], vis[i] = false;
		while (aug(src, 1e9));
	while (modlabel());
}

int main()
{
	scanf("%d%d%d%d", &tot, &m, &src, &dest);
	for (int x, y, w, f, i = 1; i <= m; i++)
		scanf("%d%d%d%d", &x, &y, &w, &f), add1(x, y, w, f);
	zkw();
	printf("%d %d\n", maxflow, mincost);
	return 0;
}

最优图像那题把这个代码xjb改改就可以A掉了,由于那题是对概率取log所以要开double(逗拨),所以可能涉及写精度问题(貌似不用判精度...不过我对实数过敏)反正那道题上zkw绝对不会卡常啦

备注:很多博客(例如olinr的博客)里的zkw费用流是假的,那是多路增广dinic,zkw费用流是要维护一些东西的

posted @ 2019-03-01 20:18  ghj1222  阅读(1098)  评论(0编辑  收藏  举报