首页 > 其他 > 详细

zkw费用流

时间:2019-03-01 20:52:30      阅读:202      评论:0      收藏:0      [点我收藏+]

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

zkw费用流

原文:https://www.cnblogs.com/oier/p/10458540.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!