差分约束系统(System Of Difference Constraints),给定一组由 \(M\) 个不等式组成的 \(N\) 元一次不等式组,形如 \(x_i - x_j \leq a_k\),可以求出这个不等式组的一组解。
观察一下 \(x_i - x_j \leq a_k\) 这个不等式,变形成 \(x_i \leq x_j + a_k\) 后,可以发现与图论中的最短路问题里的 “三角形不等式” 是一模一样的,那么直接对于每个不等式 \(x_i - x_j \leq a_k\) 从 \(j\) 点到 \(i\) 点连一条边权为 \(a_k\) 的边,然后跑最短路就行了,注意有负环的时候是无解的。
有些时候,会遇到 \(x_i - x_j \geq a_k\) 的不等式,可以转化为 \(x_j - x_i \leq a_k\) ,就跟上面一模一样了。
例题
给出 \(M\) 个形如 \(x_i - x_j \geq a_k\),\(K\) 个形如 \(x_i - x_j \leq a_k\) 的不等式,求 \(x_n - x_1\) 的最大值,如无解输出 \(-1\),差值最大可无限大输出 \(-2\),正常情况输出 \(x_n - x_1\) 的最大值。
仔细分析一下,要求 \(x_n - x_1\) 的最大值,其实就是利用给出的不等式们求出 \(x_n - x_1\) 的解集。那么,直接按上面说的方法连边,点 \(N\) 到点 \(1\) 的最短路就是答案了。注意有负环的情况就是无解,点 \(1\) 点 \(N\) 不连通就是差值最大可无限大。
丢上代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
const int Inf = 0x3f3f3f3f;
const int MaxN = 1000 + 5;
const int MaxM = 10000 + 5;
int N, M, K, Tot;
int Dist[MaxN], Cnt[MaxN], FST[MaxN];
bool Vis[MaxN];
std::queue <int> Q;
struct Linker
{
int to, w, nxt;
Linker(){}
Linker(int x, int y, int z)
{
to = y;
w = z;
nxt = FST[x];
}
} E[MaxM << 1];
inline int read()
{
register int x = 0;
register char ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x;
}
inline void AddEdge(int x, int y, int z)
{
E[++Tot] = Linker(x, y, z);
FST[x] = Tot;
}
void SPFA()
{
memset(Dist, Inf, sizeof(Dist));
Dist[1] = 1;
Q.push(1);
while(!Q.empty())
{
int from = Q.front();
Q.pop();
Vis[from] = 0;
for(int k = FST[from]; k; k = E[k].nxt)
{
int to = E[k].to, w = E[k].w;
if(Dist[to] > Dist[from] + w)
{
Dist[to] = Dist[from] + w;
Cnt[to] = Cnt[from] + 1;
if(Cnt[to] == N)
{
puts("-1");
exit(0);
}
if(!Vis[to])
{
Q.push(to);
Vis[to] = 1;
}
}
}
}
}
int main()
{
N = read();
M = read();
K = read();
for(int i = 1; i <= M; ++i)
{
int x = read(), y = read(), z = read();
AddEdge(x, y, z);
}
for(int i = 1; i <= K; ++i)
{
int x = read(), y = read(), z = read();
AddEdge(y, x, -z);
}
SPFA();
if(Dist[N] == Inf) puts("-2");
else printf("%d\n", Dist[N]);
return 0;
}