洛谷题单指南-最短路-P5960 【模板】差分约束
原题链接:https://www.luogu.com.cn/problem/P5960
题意解读:n个未知数,m个不等式,求一组可能解。
解题思路:
1、概念
差分约束系统是由一组形如xj-xi <= ck的不等式组成的系统,其中xi 和xj是变量,ck是常数。其目标是求解这个不等式组,找到一组满足所有不等式的变量取值。
2、建模
通过将不等于移项,变成形如xj <= xi + ck的形式,对于每一个不等式,通过在i->j之间连一条长度为ck的边,如果从某个起点到各个点存在一条最短路,则必然满足xj <= xi + ck,因为如果存在xj > xi + ck,在计算最短路过程中,必然会被松弛:if(xj > xi + ck) xj = xi + ck,最终一定满足xj <= xi + ck。
因此,求解不等式组的一组可能解,就变成了求所有点的最短路径,未知数xi的可能解就是dist[i]。
3、起点
要计算所有点的最短路,必须找到合适的起点,也就是从起点可以达到所有点,如果无法明确找到这样的点,不妨设0为起点,dist[0]=0,即x0=0,
从0点到所有点建一条长度为0的边,再跑一遍SPFA算法,如果不出现负环,则一组可能的解就是所有点的最短路dist[i]。
4、无解
如果在SPFA计算中出现负环,说明存在一个环(x1->x2->x3->x1)中的点有这样的关系:x2<=x1+c1, x3<=x2+c2, x1<=x3+c3,三个式子相加得到0<=c1+c2+c3,但是负环说明c1+c2+c3<0,产生矛盾,因此不等式无解。
5、变形
为了统一为最短路和负环计算,可以将不等式的关系也进行统一,
- 如果出现A = B,可以转化为A <= B, B <= A
- 如果出现A < B,可以转换为A + 1 <= B
- 如果出现A > B,可以转化为B + 1 <= A
- 如果出现A >= B,可以转化为B <= A
如果是要计算最长路和正环,则不等式的<=要变换成>=。
6、求最大值
- 如果是求某个未知数xi的最大值, 对于从0到i的路径上的所有边的不等式进行相加:
x1 <= x0
x2 <= x1 + c1
...
xi <= xi-1+ci-1
得到xi <= c1+c2+...+ci-1
要得到xi的最大值,必须求出所有可能的c1+c2+...+ci-1的最小值,也就是从0到i的最短路。
- 如果是求某两个未知数xj-xi差值的最大值,同样对于i到j路径上的所有边的不等式进行相加,不难得出结论是求从i到j的最短路。
7、求最小值
要求最小值,不等式关系需要调整为形如:xj >= xi + ck
同样建立从i到j权值为ck的边,然后跑最长路,无解对应存在正环。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 5005, M = 10005;
int h[N], e[M], w[M], ne[M], idx;
int dist[N], cnt[N];
bool vis[N];
int n, m;
void add(int a, int b, int c)
{
e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx;
}
bool spfa()
{
memset(dist, 0x3f, sizeof(dist));
queue<int> q;
q.push(0);
dist[0] = 0;
while(q.size())
{
int u = q.front(); q.pop();
vis[u] = false;
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(dist[v] > dist[u] + w[i])
{
dist[v] = dist[u] + w[i];
cnt[v] = cnt[u] + 1;
if(cnt[v] >= n + 1) return true; //存在负环
if(!vis[v])
{
q.push(v);
vis[v] = true;
}
}
}
}
return false;
}
int main()
{
memset(h, -1, sizeof(h));
cin >> n >> m;
while(m--)
{
int v, u, w;
cin >> v >> u >> w;
add(u, v, w);
}
for(int i = 1; i <= n; i++) add(0, i, 0); //虚拟源点0到所有点建立一条权值为0的边
if(spfa()) cout << "NO";
else for(int i = 1; i <= n; i++) cout << dist[i] << " ";
return 0;
}
浙公网安备 33010602011771号