P5905 卡spfa只能用dj的有负环的全源最短路
题目描述:
给定一个包含 n 个结点和 m 条带权边的有向图,求所有点对间的最短路径长度,一条路径的长度定义为这条路径上所有边的权值和。
注意:
-
边权可能为负,且图中可能存在重边和自环;
- 部分数据卡 n 轮 SPFA 算法.
思路:有负边,直接跑dj会出错,需要把边变为逻辑上的正数,不能简单得同时加上一个值,原因:here
创建一个节点,向其余节点连权值为0的边,然后以它为源点跑spfa,得到距离hi,顺便判断是否存在负环,存在负环则输出-1,
否则继续操作,将原图中的m条边进行处理,即e[i].w+=h[u]-h[v],然后再对每个点跑dj,计算结果时dis[v]要减去之前加上的h[u]-h[i];
code:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 10005; const int inf = 1000000000; const ll mod = 100000000; struct edge { int f, t, nxt; ll dis; }e[maxn]; int hd[maxn], tot; void add(int f, int t, ll dis) { e[++tot] = { f,t,hd[f],dis }; hd[f] = tot; } int t[maxn]; bool vis[maxn]; ll dis[maxn], h[maxn]; int n, m; bool spfa(int s) { memset(h, 0x3f, sizeof(h)); queue<int>q; q.push(s); h[s] = 0; vis[s] = 1; while (!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for (int i = hd[u]; i; i = e[i].nxt) { int v = e[i].t; ll w = e[i].dis; if (h[u] + w < h[v]) { h[v] = h[u] + w; if (!vis[v]) { vis[v] = 1; t[v]++; if (t[v] == n + 1)return 0;//负环 q.push(v); } } } } return 1; } struct node { int id; ll dis; bool operator<(node b)const { return dis > b.dis; } }; void dj(int s) { for (int i = 0; i <= n; i++) { dis[i] = inf; } memset(vis, 0, sizeof(vis)); priority_queue<node>q; dis[s] = 0; q.push({ s,0 }); while (!q.empty()) { int u = q.top().id; q.pop(); if (vis[u])continue; vis[u] = 1; for (int i = hd[u]; i; i = e[i].nxt) { int v = e[i].t; ll w = e[i].dis; if (dis[u] + w < dis[v]) { dis[v] = dis[u] + w; q.push({ v,dis[v] }); } } } } int main() { //freopen("test.txt", "r", stdin); scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { int a, b; ll w; scanf("%d%d%lld", &a, &b, &w); add(a, b, w); } for (int i = 1; i <= n; i++) { add(0, i, 0); } if (!spfa(0)) { printf("-1\n"); return 0; } for (int i = 1; i <= m; i++) { int u = e[i].f, v = e[i].t; e[i].dis += h[u] - h[v]; } for (int i = 1; i <= n; i++) { dj(i); ll ans = 0; for (int j = 1; j <= n; j++) { if (dis[j] == inf) {//不能到达 ans += j * dis[j]; } else { ans += j * (dis[j] + h[j] - h[i]); } } printf("%lld\n", ans); } return 0; }