Bellman可以解决负权边,也可以用来判断负权回路是否存在。
核心代码:
for (i = 1; i <= n - 1; i++) {
check = 1;
for (j = 1; j <= m; j++) {
if (dis[v[j]] > dis[u[j]] + w[j]) {
dis[v[j]] = dis[u[j]] + w[j];
check = 0;
}
}
if (check)
break;
}
完整代码:
#include <bits/stdc++.h>
using namespace std;
const int inf = 99999;
int main()
{
int n, m;
cin >> n >> m;
int dis[n + 1];
int i, j;
int u[m + 1], v[m + 1], w[m + 1];
for (i = 1; i <= m; i++) {
cin >> u[i] >> v[i] >> w[i];
}
for (i = 1; i <= n; i++) {
dis[i] = inf;
}
dis[1] = 0;
int k;
int check;
for (i = 1; i <= n - 1; i++) { //最短路径不能有回路,无论正负,即最短路径最多n-1条边
check = 1;
for (j = 1; j <= m; j++) {
if (dis[v[j]] > dis[u[j]] + w[j]) {
dis[v[j]] = dis[u[j]] + w[j];
check = 0;
}
}
if (check) //如果不再更新则break,已找到最短路径。
break;
}
int flag = 0;
for (i = 1; i <= m; i++) { //n-1次后仍能更新,则存在负权回路。
if (dis[v[i]] > dis[u[i]] + w[i])
flag = 1;
}
if (flag)
cout << "有负权回路";
else {
for (i = 1; i <= n; i++) {
cout << dis[i] << endl;
}
}
return 0;
}
/*可以输入以下数据验证,输出将是:0 -3 -1 2 4
5 5
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
*/
队列优化后的:
(可以处理负权边,也可以判断负环回路的存在)
#include <bits/stdc++.h>
using namespace std;
const int inf = 99999;
int main()
{
int n, m;
cin >> n >> m;
int num[n + 1]; //用以判断负权回路,一个点入队超过n次,即存在
memset(num, 0, sizeof(num));
int u[m + 1];
int v[m + 1];
int w[m + 1];
int first[n + 1];
int next[m + 1];
int i, j;
for (i = 1; i <= n; i++) {
first[i] = -1;
}
for (i = 1; i <= m; i++) {
cin >> u[i] >> v[i] >> w[i];
next[i] = first[u[i]];
first[u[i]] = i; //邻接表存储。
}
int dis[100];
for (i = 1; i <= n; i++)
dis[i] = inf;
dis[1] = 0;
int que[100];
int head = 1;
int tail = 1;
int book[100];
memset(book, 0, sizeof(book));
que[tail] = 1;
tail++;
book[1] = 1;
num[1]++;
int k;
//一个点v[i]的dis[v[i]]变小,可能引起其相邻点的最短路径变化。
while (head < tail) {
k = first[que[head]];
while (k != -1) {
if (dis[v[k]] > dis[u[k]] + w[k]) {
dis[v[k]] = dis[u[k]] + w[k];
if (book[v[k]] == 0) {
book[v[k]] = 1;
num[v[k]]++;
que[tail++] = v[k];
}
}
k = next[k];
}
//以下两句不要颠倒了顺序
book[que[head]] = 0; //与广搜不同,这里一个点可以重复入队。
head++; //即这里的在队列中指的是在[head,tail)之中。
} //而广搜中在队列中的是指进入到过队列中。
int MAX = 0;
for (i = 1; i <= n; i++) {
MAX = max(MAX, num[i]);
}
if (MAX > n) {
cout << "有负权回路";
} else {
for (i = 1; i <= n; i++) {
if (i > 1)
cout << " ";
cout << dis[i];
}
}
return 0;
}
/*可以用一下数据验证,输出为:0 2 5 9 9
5 7
1 2 2
1 5 10
2 3 3
2 5 7
3 4 4
4 5 5
5 3 6
*/