寒假来我们学校讲课的人讲到的黑科技= =

首先先说一个定理

在一张网络流图中,一个可行流,除了原点 $s$和汇点 $t$,所有点的入流和出流是一样的 (显然)

流量平衡等式就是基于这个的呢

首先我们先看一道例题:志愿者招募

为了简化模型,我们将上面这题描述成下面这样:

总共有 $n$天,对于第 $i$天,我们有 $k$种人工作,第 $j$种人来了 $b_j$个,这一天至少需要 $a_i$个人。

我们有

$$\sum_{j=1}^k b_j \geq a_i$$

列出 $n$个这样的式子之后,我们需要最小化

$$\sum_{i=1}^m b_ic_i$$

你会发现你把它化归成了线性规划问题。但是你会发现一种人可能会涵盖 $n$天,这样线性规划成本就很大,我们继续说我们的方法。

按照套路,我们设 $d_i\geq 0$,然后将不等式化成等式

$$\sum_{j=1}^k b_j – a_i – d_i=0$$

你大概可以脑补到我说的流量平衡等式是啥了,将每个等式看成一个点,将里面的正数设为流入边,将负数设为流出边,然后通过控制流的费用来算对 $b_ic_i$的贡献。

然而你这里一个 $b_i$会在很多个等式里被算呀,你怎么设边的费用呢?

于是我们选择差分,将两边补两个 $0=0$的,等式每个等式和上面一个等式做差,我们就会得到 $n+1$条等式,然后你会发现每一个数都会被加一次和减一次 (因为 $b_i$是连续的所以这是显然成立的,如果 $b_i$不连续这个方法就 $gg$了),然后就按上面说的方法建边就好了。

我们来结合一个具体的栗子:

第一种人 $b_1$个,单个人工资 $c_1=3$,能在 $1$到 $3$天工作

第二种人 $b_2$个,单个人工资 $c_2=2$,能在 $2$到 $3$天工作

第三种人 $b_3$个,单个人工资 $c_3=2$,能在 $1$到 $2$天工作

$a_1=5,a_2=4,a_3=3$

我们可以列出以下式子

$$0=0$$

$$b_1+b_3-d_1-a_1=0$$

$$b_1+b_2+b_3-d_2-a_2=0$$

$$b_1+b_2-d_3-a_3=0$$

$$0=0$$

差分

$$b_1+b_3-d_1-a_1=0$$

$$b_2+d_1-d_2+a_1-a_2=0$$

$$-b_3+d_2-d_3+a_2-a_3=0$$

$$-b_1-b_2+d_3+a_3=0$$

我们设流入边为负,流出边为正

因为 $b$是有费用的,看 $b_1$对等式的贡献我们就可以流一条 $1->4$的边,费用为 $c_1$,流量为 $\infty$。其它 $b$类似

对于 $a$,我们记每个式子里 $a$的部分为 $\Delta a_i$,如果 $\Delta a_i<0$就相当于是从源点向这个式子所代表的点连接一条费用为 $0$流量为 $-\Delta a_i$的边,否则就是从这个式子的点流向汇点连一条费用为 $0$流量为 $\Delta a_i$的边。

对于 $d$,它没有费用,我们用相似的建法,我们看 $d_2$就是从第三条式子所代表的点连向第二条式子所代表的点,流量为 $\infty$,费用为 $0$

然后建图跑就行了。

(我草稿本上有一个很可爱的图可惜学校没有手机拍不下来)(鸽子说: 回家再拍)

代码懒得给了

下面我们看一道更好玩的题:River Problem

这题看起来好麻烦呀要差分的话还要搞前缀和,因为在树上没准还要搞个 $lca$?

$qhy$作为老年退役猫咪刚开始是这样想的呢

但是她很容易的就发现,你这样做 $b_i$不止一次会出现在某个点所代表式子里呀,然后就咕咕咕了。

其实这题我感觉并不能列出具体的式子,你可以想象一下,对于每一条被污染的边 $(u,v)$,我们连一条 $(s,v)$和 $(u,t)$流量为污染值费用为 $0$,一个清洁剂 $(u,v,l,c)$就相当于连一条 $(v,u)$流量为 $l$费用为 $c$的边,相当于你在那条 $path$上做了差分,并且这样拆成单条 $path$的搞法也没错的呢(意会一下就相当于将一个点拆了好几个点,然后分别放在不同的 $path$上,一个点有几个式子,把这些式子合起来也满足性质)

代码

#include<bits/stdc++.h>
#define fo(i, a, b) for (int i = (a); i <= (b); ++i)
#define edge(i, u) for (int i = head[u], v = e[i].to; i; i = e[i].nxt, v = e[i].to)
#define N 10005
#define inf 1023333333
int n, m, head[N], tot = 1, ans, que[N + 5], hh, tt, dis[N], pre[N], cap[N], a[N];
bool vis[N];
int sum;
struct edge{
    int to, nxt, w, cap;
}e[N << 3];
inline void addedge (int u, int v, int w, int cap)
{
//    printf("u = %d  v = %d   w = %d   cap = %d \n", u, v, w, cap);
    e[++tot] = (edge) {v, head[u], w, cap};
    head[u] = tot;
    e[++tot] = (edge) {u, head[v], -w, 0};
    head[v] = tot;
}
inline bool spfa (int s, int t)
{
    que[1] = s; vis[s] = 1;
    hh = tt = 1;
    fo (i, 0, n + 3)
    {
        dis[i] = inf;
        cap[i] = inf;
    }
    dis[s] = 0;
    while (hh != tt + 1)
    {   
        int u = que[hh];
        edge (i, u)
            if (e[i].cap && dis[u] + e[i].w < dis[v])
            {
                pre[v] = i;
                dis[v] = dis[u] + e[i].w;
                cap[v] = std::min(cap[u], e[i].cap);
                if (!vis[v])
                {
                    vis[v] = 1;
                    tt++;
                    if (tt == N) tt -= N;
                    que[tt] = v;
                }
            }
        vis[u] = 0;
        hh++;
        if (hh == N) hh -= N;
    }
    return dis[t] != inf;
}
inline void updata (int s, int t)
{
    int i = t;
    while (i != s)
    {
        e[pre[i]].cap -= cap[t];
        e[pre[i] ^ 1].cap += cap[t];
        i = e[pre[i] ^ 1].to;
    }
    ans += dis[t] * cap[t];
    sum += cap[t];
}
inline void costflow (int s, int t)
{
    while (spfa(s, t))
        updata(s, t);
}
int main ()
{
    int T;
    scanf("%d", &T);
    fo (qwq, 1, T)
    {
        memset(e, 0, sizeof e); tot = 1;
        memset(head, 0, sizeof head);
        ans = sum = 0;
        int psum = 0;
        printf("Case #%d: ", qwq);
        scanf("%d", &n);
        int s = 0, t = n + 1;
        fo (i, 2, n)
        {
            int u, v, p;
            scanf("%d %d %d", &u, &v, &p);
            addedge(u, v, 0, inf);
            addedge(u, t, 0, p);
            addedge(s, v, 0, p);
            psum += p;
        }
        scanf("%d", &m);
        fo (i, 1, m)
        {
            int u, v, cap, c;
            scanf("%d %d %d %d", &u, &v, &cap, &c);
            addedge(v, u, c, cap);
        }
        costflow(s, t);
        if (sum != psum)
            printf("-1\n");
        else
            printf("%d\n", ans);
    }
    return 0;
}
分类: 文章

HomuraCat

明日は何になる? やがて君になる!

1 条评论

Remmina · 2019年2月22日 12:02 上午

似乎学过,但是不记得了_(:з」∠)_
Mark 一下明天(严格来说是今天)学

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注