简单叙述用Dijkstra求费用流

Dijkstra不能求有负权边的最短路。

类似于Johnson算法,我们也可以设计一个势函数,以满足在与原图等价的新图中的边权非负。

但是这个算法并不能处理有负圈的情况(可能需要消圈算法)。

对网络\(G\)中的每一个点设置一个势函数\(h(u)\),在任意残留网络G'的任意边\((u, v)\)都需要满足\(w_{u, v} + h(u) - h(v) \ge 0\)。

令图G的对偶图(不知道能不能这么说)为\(G'\),其对应的边\((u, v)\)的权值为\(w'_{u, v} = w_{u, v} + h(u) + h(v)\)

对于原图中的任意一条路径\((u_1, u_2, \cdots, u_k)\),它在\(G\)中的权值为\(w_{u_1, u_2} + w_{u_2, u_3} + \cdots + w_{u_{k - 1}, u_k}\),在\(G'\)中的权值为

\(w'_{u_1, u_2} + w'_{u_2, u_3} + \cdots + w'_{u_{k - 1}, u_k}\)

\(= w_{u_1, u_2} + w_{u_2, u_3} + \cdots + w_{u_{k - 1}, u_k} + h(u_1) - h(u_2) + h(u_2) - h(u_3) + \cdots + h(u_{k - 1}) - h(u_k)\)

\(= w_{u_1, u_2} + w_{u_2, u_3} + \cdots + w_{u_{k - 1}, u_k} + h(u_1) - h(u_k)\)

所以,我们在\(G'\)求出的路径都可以对应到\(G\)上,令\(dist_{u, v}\)为图\(G\)点\(u\)到点\(v\)的最短路径,\(dist'_{u, v}\)为图\(G'\)点\(u\)到点\(v\)的最短路径,显然有\(dist_{u, v} = dist'_{u, v} - h(u) + h(v)\)

所以我们只需要求\(G'\)的最短路径,就能对应回原图的最短路径。

若网络\(G\)初始边权非负,我们可令\(h(u) = 0\)

否则我们令\(h(u) = dist_{s, u}\),这个可以用Bellman-Ford算法解决。(这与Johnson算法是一模一样的)

我们考虑怎么维护势函数\(h(u)\)。

令网路\(G\)上\(dist_{s, u} = d_u\),网路\(G'\)上\(dist'_{s, u} = d'_u\)

令残余网路\(G'\)上新的势函数为\(h'(u)\)。

对于残余网络上的一条边\((u, v)\),有两种可能:

\((u, v) \in G\),那么有\(d_u + w_{u, v} + h(u) - h(v) \ge d_v\)

移项得\(w_{u, v} + (h(u) + d_u) - (h(v) + d_v) \ge 0\)

\((u, v) \in G\),那么\((v, u) \in G的增广路\),就有\(d_v + w_{v, u} + h(v) - h(u) = d_u\)

移项得\(-w_{v, u} + (h(u) + d_u) - (h(v) - d_v) = 0\)

由\(w_{u, v} = -w_{v, u}\)可得\(w_{u, v} + (h(u) + d_u) - (h(v) - d_v) = 0\)

也就有\(w_{u, v} + (h(u) + d_u) - (h(v) - d_v) \ge 0\)

所以我们不妨令\(h'(u) = h(u) + d_u\),这就维护好了势函数\(h(u)\)

luogu P3381 【模板】最小费用最大流

单路增广

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;

#define fi first
#define se second
#define mp make_pair const int N = 5005, M = 50005;
const int INF = ~0u >> 1; int n, m, s, t;
int tot = -1, head[N];
struct Edge {
int p, nxt, c, w;
Edge(int p = 0, int nxt = 0, int c = 0, int w = 0) : p(p), nxt(nxt), c(c), w(w) {}
} edge[M * 2];
inline void Add_Edge(int u, int v, int c, int w) {
edge[++tot] = Edge(v, head[u], c, w);
head[u] = tot;
return;
} int h[N], d[N], dc[N], pr[N];
priority_queue<PII> pq; bool Dijkstra(int s, int t, int &mf, int &mc) {
fill(d + 1, d + n + 1, INF);
dc[s] = INF;
d[s] = 0;
pr[s] = -1;
pq.push(mp(0, s));
while (!pq.empty()) {
PII cur = pq.top();
pq.pop();
int u = cur.se;
if (-cur.fi > d[u]) continue;
for (int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].p, c = edge[i].c, w = edge[i].w + h[u] - h[v];
if (!c) continue;
if (d[v] > d[u] + w) {
d[v] = d[u] + w;
pr[v] = i;
dc[v] = min(dc[u], c);
pq.push(mp(-d[v], v));
}
}
}
if (d[t] == INF) return 0;
for (int i = 1; i <= n; ++i)
if (d[i] < INF) h[i] += d[i];
int c = dc[t];
mf += c;
mc += c * h[t];
for (int x = t; ~pr[x]; x = edge[pr[x] ^ 1].p) {
edge[pr[x]].c -= c;
edge[pr[x] ^ 1].c += c;
}
return 1;
} int main() {
scanf("%d%d%d%d", &n, &m, &s, &t);
memset(head, -1, sizeof(head));
for (int i = 1; i <= m; ++i) {
int u, v, c, w;
scanf("%d%d%d%d", &u, &v, &c, &w);
Add_Edge(u, v, c, w);
Add_Edge(v, u, 0, -w);
}
int mf = 0, mc = 0;
while (Dijkstra(s, t, mf, mc));
printf("%d %d\n", mf, mc);
return 0;
}

多路增广(复杂度不变)

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;

#define fi first
#define se second
#define mp make_pair const int N = 5005, M = 50005;
const int INF = ~0u >> 1; int n, m, s, t;
int tot = -1, head[N], cur[N];
struct Edge {
int p, nxt, c, w;
Edge(int p = 0, int nxt = 0, int c = 0, int w = 0) : p(p), nxt(nxt), c(c), w(w) {}
} edge[M * 2];
inline void Add_Edge(int u, int v, int c, int w) {
edge[++tot] = Edge(v, head[u], c, w);
head[u] = tot;
return;
} int h[N], d[N];
priority_queue<PII> pq; bool Dijkstra(int s, int t) {
fill(d + 1, d + n + 1, INF);
pq.push(mp(d[s] = 0, s));
while (!pq.empty()) {
PII cur = pq.top();
pq.pop();
int u = cur.se;
if (-cur.fi > d[u]) continue;
for (int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].p, c = edge[i].c, w = edge[i].w + h[u] - h[v];
if (c && d[v] > d[u] + w) pq.push(mp(-(d[v] = d[u] + w), v));
}
}
return d[t] < INF;
} int v[N]; int DFS(int u, int c, int t) {
if (u == t) return c;
int r = c;
v[u] = 1;
for (int &i = cur[u]; ~i && r; i = edge[i].nxt) {
int v = edge[i].p, c = edge[i].c, w = edge[i].w + h[u] - h[v];
if (!::v[v] && c && d[u] + w == d[v]) {
int x = DFS(v, min(r, c), t);
r -= x;
edge[i].c -= x;
edge[i ^ 1].c += x;
}
}
v[u] = 0;
return c - r;
} int main() {
scanf("%d%d%d%d", &n, &m, &s, &t);
memset(head, -1, sizeof(head));
for (int i = 1; i <= m; ++i) {
int u, v, c, w;
scanf("%d%d%d%d", &u, &v, &c, &w);
Add_Edge(u, v, c, w);
Add_Edge(v, u, 0, -w);
}
int mf = 0, mc = 0;
while (Dijkstra(s, t)) {
memcpy(cur, head, sizeof(cur));
int c = DFS(s, INF, t);
for (int i = 1; i <= n; ++i)
if (d[i] < INF) h[i] += d[i];
mf += c;
mc += c * h[t];
}
printf("%d %d\n", mf, mc);
return 0;
}

费用流 Dijkstra 原始对偶方法(primal-dual method)的更多相关文章

  1. 【BZOJ4842】[Neerc2016]Delight for a Cat 线性规划+费用流

    [BZOJ4842][Neerc2016]Delight for a Cat Description ls是一个特别堕落的小朋友,对于n个连续的小时,他将要么睡觉要么打隔膜,一个小时内他不能既睡觉也打 ...

  2. HDU 6611 K Subsequence(Dijkstra优化费用流 模板)题解

    题意: 有\(n\)个数\(a_1\cdots a_n\),现要你给出\(k\)个不相交的非降子序列,使得和最大. 思路: 费用流建图,每个点拆点,费用为\(-a[i]\),然后和源点连边,和后面非降 ...

  3. UVA 10806 Dijkstra, Dijkstra.(费用流)

    n个点的无向带权图,求1->n的最短往返路径,不走重复边. 这里涉及到一个知识点:求无向图上s->t的最短路,其实就是费用流. 而求1->n最短往返路径呢?增加源点s,由s到1加弧, ...

  4. 网络流板子/费用流板子 2018南京I题+2016青岛G题

    2018南京I题: dinic,链式前向星,数组队列,当前弧优化,不memset全部数组,抛弃满流点,bfs只找一条增广路,每次多路增广 #include <bits/stdc++.h> ...

  5. 【P2405】方格取数问题加强版(费用流)

    考虑如何建图.还是老样子先拆点,然后把每两个点之间连接两条边,一条流量为1,费用为-点权,处理是否走这个点.一条流量无限,没有费用,因为哪怕一个点选过了,它的地方还是可以重复走过去的. 然后把经由一个 ...

  6. 洛谷 1004 dp或最大费用流

    思路: dp方法: 设dp[i][j][k][l]为两条没有交叉的路径分别走到(i,j)和(k,l)处最大价值. 则转移方程为 dp[i][j][k][l]=max(dp[i-1][j][k-1][l ...

  7. 【BZOJ-3638&3272&3267&3502】k-Maximum Subsequence Sum 费用流构图 + 线段树手动增广

    3638: Cf172 k-Maximum Subsequence Sum Time Limit: 50 Sec  Memory Limit: 256 MBSubmit: 174  Solved: 9 ...

  8. [bzoj4514]数字配对[费用流]

    今年SDOI的题,看到他们在做,看到过了一百多个人,然后就被虐惨啦... 果然考试的时候还是打不了高端算法,调了...几天 默默地yy了一个费用流构图: 源连所有点,配对的点连啊,所有点连汇... 后 ...

  9. 【wikioi】1033 蚯蚓的游戏问题(费用流)

    http://wikioi.com/problem/1033/ 这题也是很水的费用流啊,同之前那题一样,拆点然后建边,容量为1,费用为点权.然后建个源连第一行每个点,容量为1,费用为0,然后最后一行每 ...

随机推荐

  1. rsync启动脚本

    rsync启动脚本 01 #!/bin/bash  www.ahlinux.com 02 # 03 # rsyncd      This shell script takes care of star ...

  2. python一些包

    jieba结巴 github上已经够详细了https://github.com/vinta/awesome-python

  3. 数据科学中的R和Python: 30个免费数据资源网站

    1 政府数据 Data.gov:这是美国政府收集的数据资源.声称有多达40万个数据集,包括了原始数据和地理空间格式数据.使用这些数据集需要注意的是:你要进行必要的清理工作,因为许多数据是字符型的或是有 ...

  4. 锋利的jQuery(第二版) 初读笔记

    window.onload(): 必须等待网页中所有的内容加载完毕后(包括图片)才能执行. $(document).ready(): 网页中所有DOM结构绘制完毕后就执行,可能DOM元素关联的东西并没 ...

  5. java:struts框架4(Ajax)

    1.Ajax: 先导入jar包: struts.xml: <?xml version="1.0" encoding="UTF-8"?> <!D ...

  6. MyBatis框架原理3:缓存

    上一篇[MyBatis框架原理2:SqlSession运行过程][1]介绍了MyBatis的工作流程,其中涉及到了MyBatis缓存的使用,首先回顾一下工作流程图: 如果开启了二级缓存,数据查询执行过 ...

  7. Unity 动画属性

    在动画的使用上使用不当的设置往往会造成不可预料的结果. 首先,如果动画自身可以驱动物体移动,那么在Animator组件上必须选择apply root motion,物体的动画位移才能生效,否则动画只能 ...

  8. python 一周总结

    从7月27前就开始了Djangoj框架的学习到今天一个项目实战已经过了一半了, django给我的感觉就像一个成熟的mov 首先是是数据模型层,Django已经成熟的加入了很多的数据模型供我们所使用 ...

  9. 【VS开发】使用 NuGet 管理项目库

    NuGet 使用 NuGet 管理项目库 Phil Haack 无论多么努力,Microsoft 也没办法提供开发人员所需要的每一个库. 虽然 Microsoft 在全球的员工人数接近 90,000, ...

  10. oracle truncate表 恢复操作

    truncate恢复表 1.创建测试用表 conn elan/elan create table haha as select * from dba_users; 2.查询表数据 ) from hah ...