Johnson

时间:2025-04-08 07:24:09
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 10; const int INF = 1e9; // 定义边的结构体,包含目标节点 v 和边的权重 w struct edge { int v, w; }; // 邻接表存储图,e[i] 存储从节点 i 出发的所有边 vector<edge> e[N]; // vis 数组用于标记节点是否在队列中,cnt 数组用于记录每个节点入队的次数 int vis[N], cnt[N]; // h 数组用于存储从虚拟源点到各节点的最短路,d 数组用于存储从某个源点到各节点的最短路 long long h[N], d[N]; int n, m; // 使用 SPFA 算法计算从虚拟源点 0 到其他所有节点的最短路 void spfa() { // 定义一个队列用于存储待处理的节点 queue<int> q; // 初始化 h 数组为一个很大的值,memset(63) 相当于将每个元素初始化为一个较大的数 memset(h, 63, sizeof h); memset(vis, false, sizeof vis); // 虚拟源点到自身的距离为 0 h[0] = 0; // 标记虚拟源点在队列中 vis[0] = 1; // 将虚拟源点加入队列 q.push(0); while (q.size()) { int u = q.front(); q.pop(); // 标记该节点不在队列中 vis[u] = 0; // 遍历从节点 u 出发的所有边 for (auto ed : e[u]) { // 获取目标节点 v 和边的权重 w int v = ed.v, w = ed.w; // 如果通过节点 u 到达节点 v 的距离更短,则更新 h[v] if (h[v] > h[u] + w) { h[v] = h[u] + w; // 更新节点 v 的入队次数 cnt[v] = cnt[u] + 1; // 如果某个节点入队次数超过 n 次,说明存在负环 if (cnt[v] > n) { printf("-1\n"); // 存在负环,程序退出 exit(0); } // 如果节点 v 不在队列中,则将其加入队列 if (!vis[v]) { q.push(v); vis[v] = 1; } } } } } // 使用 Dijkstra 算法计算从源点 s 到其他所有节点的最短路 void dijkstra(int s) { // 定义一个优先队列,用于存储待处理的节点,按照距离从小到大排序 priority_queue<pair<long long, int>> q; // 初始化 d 数组为无穷大 for (int i = 1; i <= n; i++) { d[i] = INF; } // 初始化 vis 数组为 false,表示所有节点都未被处理 memset(vis, false, sizeof vis); // 源点到自身的距离为 0 d[s] = 0; // 将源点加入优先队列 q.push({ 0,s }); // 当优先队列不为空时进行处理 while (q.size()) { // 取出队首节点 int u = q.top().second; q.pop(); // 如果该节点已经被处理过,则跳过 if (vis[u]) { continue; } // 标记该节点已被处理 vis[u] = 1; // 遍历从节点 u 出发的所有边 for (auto ed : e[u]) { // 获取目标节点 v 和边的权重 w int v = ed.v; int w = ed.w; // 如果通过节点 u 到达节点 v 的距离更短,则更新 d[v] if (d[v] > d[u] + w) { d[v] = d[u] + w; // 如果节点 v 未被处理,则将其加入优先队列 if (!vis[v]) { q.push({ -d[v],v }); } } } } } int main() { cin >> n >> m; // 输入每条边的信息,并将其加入邻接表 for (int i = 0; i < m; i++) { int a, b, c; cin >> a >> b >> c; e[a].push_back({ b,c }); } // 从虚拟源点 0 向其他所有节点添加一条边权为 0 的边 for (int i = 1; i <= n; i++) { e[0].push_back({ i,0 });//加虚拟边 } // 调用 SPFA 算法计算从虚拟源点到其他所有节点的最短路 spfa(); // 对图的边权进行重新计算,确保所有边权非负 for (int u = 1; u <= n; u++) { for (auto& ed : e[u]) { ed.w += h[u] - h[ed.v];//构造新边 } } // 以每个节点为源点,调用 Dijkstra 算法计算最短路 for (int i = 1; i <= n; i++) { dijkstra(i); long long ans = 0; // 计算从源点 i 到其他所有节点的最短路的加权和 for (int j = 1; j <= n; j++) { // 如果节点 j 不可达,则使用无穷大计算 if (d[j] == INF) { ans += (long long)j * INF; } // 否则,使用实际距离计算 else { ans += (long long)j * (d[j] + h[j] - h[i]); } } // 输出结果 printf("%lld\n", ans); } return 0; }