输入样例
3 3
1 2 3
2 3 -2
1 3 1
实现思路
这题用到了一种名为差分约束系统的思想,将差分约束转化为图论问题。我个人理解为求交集,即求上界时为求所有上界集合的最小值(即最短路径),求下界时为求下界集合的最大值(即最长路径, 可以用SPFA改 松弛方向来实现)。
差分约束的思想可以参考这篇博客:差分约束入门
实现代码
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 5e3 + 5;
const int Inf = 0x3f3f3f3f;
typedef pair<int, int> Edge;
vector<Edge> edge[maxn];
int n, m;
int dis[maxn], inq[maxn], clc[maxn];
bool spfa(int u) {
memset(dis, Inf, sizeof(dis));
memset(inq, 0, sizeof(inq));
memset(clc, 0, sizeof(clc)); // 用来判断是否存在负环,遍历点达到n时即存在环
dis[u] = 0, inq[u] = 1;
queue<int> q;
q.push(u);
while (q.size()) {
u = q.front(); q.pop(); inq[u] = 0;
for (int i = 0; i < edge[u].size(); i++) {
int v = edge[u][i].first, d = edge[u][i].second;
if (dis[v] > dis[u] + d) {
dis[v] = dis[u] + d;
if (!inq[v]) {
q.push(v);
inq[v] = 1;
if (++clc[v] == n) return false; // 存在负环,返回
}
}
}
}
return true;
}
int main() {
int u, v, y;
cin >> n >> m;
for (int i = 1; i <= n; i++) edge[0].push_back(Edge(i, 0)); // 这里约束到一个超级原点来使得各个dis同时符合约束条件
for (int i = 1; i <= m; i++) {
cin >> v >> u >> y;
edge[u].push_back(Edge(v, y));
}
if (!spfa(0)) cout << "NO" << endl;
else for (int i = 1; i <= n; i++) cout << dis[i] << " ";
return 0;
}