johnson最短路
https://www.starrycoding.com/problem/100
关键就在于,解决dijkstra不能处理负权值的问题,先用spfa或者bellman-ford得到所有点到虚点的最短距离,可以称之为势能。然后通过势能对所有边进行处理,处理过后保证了所有边都是正的,再对每个点跑dijkstra就能实现全源带负权的最短路。跑完后再通过势能还原回来。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 3e3 + 5;
const ll inf = 4e18 + 5;
int n, m;
struct Edge
{
ll y, w;
bool const operator < (const Edge& a)const
{
return w == a.w ? y < a.y : w > a.w;//注意此处大于号
}
};
vector<Edge>g[M];
ll d[M][M];//记录全源最短路
ll h[M];//虚点的单源最短路(势能)
bitset<M>vis;
queue<int>q;
int cnt[M];
int spfa(int st)
{
q.push(0);
vis[0] = 1;
while(q.size())
{
int x = q.front();
q.pop();
vis[x] = 0;
for (auto& [y, w] : g[x])
{
if (h[y] > h[x] + w)
{
h[y] = h[x] + w;
cnt[y]++;
if (cnt[y] >= n)
return 0;
if (!vis[y])
{
vis[y] = 1;
q.push(y);
}
}
}
}
return 1;
}
void dijkstra(int st, ll d[])
{
for (int i = 1; i <= n; i++)
d[i] = inf;
priority_queue<Edge>pq;
vis.reset();
pq.push({ st,d[st] = 0 });
while (pq.size())
{
int x = pq.top().y; pq.pop();
if (vis[x])continue;
vis[x] = true;
for (auto &[y, w] : g[x])
{
if (d[y] > d[x] + w)
{
d[y] = d[x] + w;
pq.push({ y, d[y] });
}
}
}
//还原回来
for (int i = 1; i <= n; ++i)d[i] = d[i] - h[st] + h[i];
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
ll x, y, z;
cin >> x >> y >> z;
g[x].push_back({ y,z });
}
//创建虚点0,默认0到所有点的距离是0
for (int i = 1; i <= n; i++)
{
g[0].push_back({ i,0 });
h[i] = inf;
}
//spfa得出虚点最短路
if (!spfa(0))//如果存在负环直接跳出
{
cout << "starrycoding" << '\n';
return;
}
//将所有边进行改动
for (int i = 1; i <= n; i++)
{
for (auto& [y, w] : g[i])//一定要加引用!!
w = w + h[i] - h[y];
}
//每个点跑一遍 dijkstra
for (int i = 1; i <= n; i++)
dijkstra(i, d[i]);
int q;
cin >> q;
while (q--)
{
int x, y;
cin >> x >> y;
//有的点可能被更新过,所以此处用inf/2
if (d[x][y] >= inf / 2)cout << "noway" << '\n';
else
cout << d[x][y] << '\n';
}
}
int main()
{
std::ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}
其他博主比较好的文章
https://blog.youkuaiyun.com/hzf0701/article/details/107697261