题目链接:https://www.luogu.com.cn/problem/P5960
差分约束系统定义:
现有
给出若干个形如的不等式约束,其中
求是否存在一组关于到的解
解法:
首先给出结论:
解差分约束系统需要联系到图论中的最短路:
现建立一张图有n个结点,标号为1-n
对于每条不等式
我们建立一条结点j指向结点i,权值为的有向边
再建立一超级源点标号为n+1(标号任意,这里为了方便标为n+1)
(这里建立超级源点的目的是为了保证连通性,从起点可以到达图中的任意一个点,从而找出图中的全部最短路)
则可以定义数组d[n]
d[u]为超级源点到u号结点的最短路
将d[i]分别与不等式中的对应
则我们有:d[i]-d[j]<=
也就是说,若超级源点到每个结点的最短路均存在,则该差分约束系统有解
此时,只需要给到超级源点的距离赋一个初值,即可以求出全部d[i],且刚好是全部取等的情况
那么当且仅当该图中存在负环时,最短路不存在,该差分约束系统无解
解法的证明:
假设现有三个结点1,2,3,存在边:
2->1,权值为a,
3->2,权值为b
3->1,权值为c
由于这里图已连通,所以就不再建立超级源点,不妨选取结点1作为起点
现对于不等式组:
则由最短路可知:
则要求结点2和结点3的最短路
显然就是要满足:
d[2]<=d[3]+b
d[1]<=d[3]+c
d[1]<=d[2]+b
三条不等式的交集
这刚好对应上面关于x的不等式组
由于每个结点都可以作为起点,那么这里可以推广到n个结点的情况
tips:
当我们遇到时
只需要两边同乘-1,然后建立一条i结点指向j结点,权值为的有向边即可
当我们遇到时
只需要建立两条i指向j,j指向i的权值为0的边即可
因为这相当于约束了
与
负环的判定方法:
bellmanford或spfa均可,记得建立超级源点保证图的连通性,后者时间复杂度更低
spfa实现代码如下:
#include <iostream>
#include <queue>
#include <stack>
using namespace std;
const int N = 5005, M = 10010,INF=(1<<30);
int to[M], w[M], nxt[M], h[N], tot;
int dis[N], vis[N], cnt[N];
int n, m;
queue<int> q;
void add(int a, int b, int c) {
to[++tot] = b;
w[tot] = c;
nxt[tot] = h[a];
h[a] = tot;
}
bool spfa() {
q.push(n + 1); dis[n + 1] = 0; vis[n + 1] = true; cnt[n + 1] = 1;
int u;
while (!q.empty()) {
u = q.front(); q.pop();
vis[u] = false;
for (int i = h[u], v; v = to[i]; i = nxt[i]) {
if (dis[v] > dis[u] + w[i]) {
dis[v] = dis[u] + w[i];
cnt[v] = cnt[u] + 1;
if (cnt[v] > n+1) return false;
if (!vis[v]) {
q.push(v);
vis[v] = true;
}
}
}
}
return true;
}
int main() {
cin >> n >> m;
for (int i = 1,a,b,c; i <= n; i++) {
cin >> a >> b >> c;
add(b, a, c);
}
for (int i = 1; i <= n; i++) {
dis[i] = INF;
add(n + 1, i, 0);
}
if (spfa()) {
for (int i = 1; i <= n; i++) cout << dis[i] << ' ';
}
else cout << "NO";
return 0;
}
附:
新手题:https://www.luogu.com.cn/problem/P1993
实现代码:
int main() {
cin >> n >> m;
for (int i = 1,j,a,b,c; i <= m; i++) {
cin >> j;
if (j == 1) {
cin >> a >> b >> c;
add(a, b, -c);
}
else if (j == 2) {
cin >> a >> b >> c;
add(b, a, c);
}
else {
cin >> a >> b;
add(a, b, 0);
add(b, a, 0);
}
}
for (int i = 1; i <= n; i++) {
add(n + 1, i, 0);
dis[i] = INF;
}
if (spfa()) cout << "Yes";
else cout << "No";
return 0;
}