一、背景
dijkstradijkstradijkstra 不能处理负边权,比如这个图就会挂掉。
它的过程是 1→2,1→31 \rightarrow 2, 1 \rightarrow 31→2,1→3,然后就结束了。
为了处理有负权的图,我们要想一个优秀的东西去解决它。
所以在这里引入一个 NBNBNB 的东西: 势能函数 (h)(h)(h)。
二、思路
先不考虑实现,我们通过一些 NBNBNB 的操作,将 w[i][j]w[i][j]w[i][j] 全部改为 w[i][j]+h[i]−h[j]w[i][j] + h[i] - h[j]w[i][j]+h[i]−h[j] 并保证其为非负数。这时
s⇝t=s→v1+v1→v2+...+vn→t=w[s][v1]+h[s]−h[v1]+w[v1][v2]+h[v1]−h[v2]+...+w[vn][t]+h[vn]−h[t]=w[s][v1]+w[v1][v2]+...+w[vn][t]+h[s]−h[t]\begin{aligned} s \leadsto t &= s \rightarrow v_1 + v_1 \rightarrow v_2 + ... + v_n \rightarrow t \\&= w[s][v_1] + h[s] - h[v_1] + w[v_1][v_2] + h[v_1] - h[v_2] + ... + w[v_n][t] + h[v_n] - h[t] \\&= w[s][v_1] + w[v_1][v_2] + ... + w[v_n][t] + h[s] - h[t] \end{aligned}s⇝t=s→v1+v1→v2+...+vn→t=w[s][v1]+h[s]−h[v1]+w[v1][v2]+h[v1]−h[v2]+...+w[vn][t]+h[vn]−h[t]=w[s][v1]+w[v1][v2]+...+w[vn][t]+h[s]−h[t]
所以任意一条 s⇝ts \leadsto ts⇝t 被影响的值都是 h[s]−h[t]h[s] - h[t]h[s]−h[t],所以修改图上的最短路等于原图上的最短路。
现在考虑构造一个 hhh 数组。
三、实现
容易想到 ∀i∈E,w[si][ti]+h[si]−h[ti]≥0\forall i \in \mathbb{E}, w[s_i][t_i] + h[s_i] - h[t_i] \geq 0∀i∈E,w[si][ti]+h[si]−h[ti]≥0 即 h[si]+w[si][ti]≥h[ti]h[s_i] + w[s_i][t_i] \geq h[t_i]h[si]+w[si][ti]≥h[ti],一个明显的差分约束是不是?h[t]h[t]h[t] 的上界是最小的 h[s]+w[s][t]h[s] + w[s][t]h[s]+w[s][t],所以跑一个最短路就能求出当前图的一个满足要求的 hhh,这时由于有负边,所以要用 spfaspfaspfa。
可是我们就是要摆脱 SPFASPFASPFA 上界为 nmnmnm 的梦魇,现在不是又回来了吗?所以我们要利用上一次的 hhh 调整下一次的 hhh。
由于这玩意就只是一个单纯的构造,这里直接给出一种构造方法。
在当前的修改图上跑一个 dist[u]dist[u]dist[u] ,记录 s⇝us \leadsto us⇝u 的最短路,下一个图的 hhh 为 h[u]+dist[u]h[u] + dist[u]h[u]+dist[u]。
正确性:
根据三的结论(即差分约束正确性),我们可以知道:在图不变的情况下,这样的 hhh 一定是对的,但是由于增广,会有一些反向边被搞进来。
如上图,假设 s→v→ts \rightarrow v \rightarrow ts→v→t 为最短路径 (l)(l)(l),那么可能会使 hhh 有问题的边一定是 lll 上的反向边。
因为是最短路,所以 ∀i∈L,h[ti]=h[si]+w[si][ti]\forall i \in \mathbb{L}, h[t_i] = h[s_i] + w[s_i][t_i]∀i∈L,h[ti]=h[si]+w[si][ti]
又 ∵w[x][y]=−w[y][x]\because w[x][y] = -w[y][x]∵w[x][y]=−w[y][x]
∴h[ti]=h[si]−w[ti][si]⇒h[si]=h[ti]+w[ti][si]\therefore h[t_i] = h[s_i] - w[t_i][s_i] \Rightarrow h[s_i] = h[t_i] + w[t_i][s_i]∴h[ti]=h[si]−w[ti][si]⇒h[si]=h[ti]+w[ti][si]
所以也满足要求。
四、参考代码
const int Maxn = 1e5;
const int Maxm = 1e7;
const LL Limit = 1e14;
const LL Inf = 0x3f3f3f3f3f3f3f;
struct Date {
int x, y; LL flux, val;
Date () {}
Date (int _x, int _y, LL _flux, LL _val) {
x = _x; y = _y; flux = _flux; val = _val;
}
};//存储边
struct edge {
int to[Maxm * 2 + 5], Next[Maxm * 2 + 5]; LL flux[Maxm * 2 + 5], val[Maxm * 2 + 5];
int len, Head[Maxn + 5];
edge () { len = 1; memset (Head, 0, sizeof Head); }
void Init () { len = 1; memset (Head, 0, sizeof Head); }
void plus (int x, int y, LL _flux, LL _val) {
to[++len] = y;
flux[len] = _flux;
val[len] = _val;
Next[len] = Head[x];
Head[x] = len;
}
void add (int x, int y, LL _flux, LL _val) {
plus (x, y, _flux, _val);
plus (y, x, 0, -_val);
}
void rev_add (int x, int y, LL _flux, LL _val) {
plus (x, y, 0, _val);
plus (y, x, _flux, -_val);
}
};//链式前向星
struct Max_Flow {
edge mp;
Date e[Maxm + 5];
int n, m, s, t;
bool vis[Maxn + 5];
int hh, tt, q[Maxn + 5];
LL dist[Maxn + 5];
int fa[Maxn + 5];//记录来边的编号
void Init () {//初始化
mp.Init ();
hh = 1; tt = 0;
n = m = s = t = 0;
memset (vis, 0, sizeof vis);
memset (dist, 0x3f, sizeof dist);
}
LL Update () {//修改增广路上的边
int p = t; LL cost = 0, flow = Inf;
while (p != s) {
cost += mp.val[fa[p]];
flow = Min (flow, mp.flux[fa[p]]);
p = mp.to[fa[p] ^ 1];
}
p = t;
while (p != s) {
mp.flux[fa[p]] -= flow;
mp.flux[fa[p] ^ 1] += flow;
p = mp.to[fa[p] ^ 1];
}
return cost * flow;
}
void Build_Positive () {//建图
mp.Init ();
rep (i, 1, m)
mp.add (e[i].x, e[i].y, e[i].flux, e[i].val);
}
LL h[Maxn + 5];
void Spfa_For_Dijkstra () {//求出初始 h (spfa 模板)
memset (vis, 0, sizeof vis);
memset (dist, 0x3f, sizeof dist);
hh = 1; tt = 0; q[++tt] = s; dist[s] = 0;
while (hh <= tt) {
int u = q[hh++];
vis[u] = 0;
for (int i = mp.Head[u]; i; i = mp.Next[i]) {
int v = mp.to[i]; LL flux = mp.flux[i], w = mp.val[i];
if (flux == 0) continue;
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
if (!vis[v]) {
q[++tt] = v;
vis[v] = 1;
}
}
}
}
rep (i, 1, n)
if (h[i] + dist[i] < Limit)
h[i] += dist[i];
}
bool Dijkstra () {//求出新的 h 和增广路(dijkstra 模板)
memset (vis, 0, sizeof vis);
memset (dist, 0x3f, sizeof dist);
priority_queue <PII, vector <PII>, greater <PII> > p;
p.push (MP (0, s)); dist[s] = 0;
while (p.size ()) {
PII tmp = p.top (); p.pop ();
int u = tmp.se;
if (vis[u]) continue; vis[u] = 1;
for (int i = mp.Head[u]; i; i = mp.Next[i]) {
int v = mp.to[i]; LL flux = mp.flux[i], w = mp.val[i] + h[u] - h[v];
if (flux == 0) continue;
if (vis[v]) continue;
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
fa[v] = i;
p.push (MP (dist[v], v));
}
}
}
rep (i, 1, n)//修改 h
if (h[i] + dist[i] < Limit)
h[i] += dist[i];
return dist[t] <= Limit;
}
LL Cost_Positive_Dijkstra () {
Build_Positive ();
Spfa_For_Dijkstra ();
LL res = 0;
while (Dijkstra ()) {//一直找到没有增广路
res += Update ();
}
return res;
}
}G;