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费用流是要维护一些东西的