一、Floyd-Warshall算法
1、可以存在负权值的边,但不可存在负环;Floyd更适用于稠密图
2、对于图的最短路径满足最优子结构:路径p是从i到j的一条最短路径,结点k是路径p上的中间结点,那么从i到k是一条最短路径、从k到j也是一条最短路径
在动态规划过程中,以每一个点为k点(中介),看是否可以松弛dis数组
int dis[maxn][maxn];
int pre[maxn][maxn];
memset(dis, 0x3f, sizeof(dis));
for (int i = 0; i < m; i++)
{
int u, v, w;
cin >> u >> v >> w;
dis[u][v] = w;
}
for (int i = 1; i <= n; i++)
{
dis[i][i] = 0;
}
for (int k = 1; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (dis[i][j] > dis[i][k] + dis[k][j])
{
dis[i][j] = dis[i][k] + dis[k][j];
pre[i][j] = k;
}
}
}
}
3、计算完成后,dis数组记录两点间最短距离,pre用于记录路径
pre数组使用方法:如果想找1->0的最短路径,那么就是pre[1][0]=2、pre[1][2]=3、pre[1][3]=1路径回溯可知0->2->3->1
二、Johnson算法
1、对稀疏图来说,Johnson算法的渐进表现要优于Floyd,并且Johnson算法可以判断负环
2、Johnson算法使用的技术称为重新赋予权值,工作原理如下:如果所有边权值都是非负值,那么我们可以对每个结点运行一次Dijkstra来找到最短路径;如果图G包含权重为负值的边,但是没有负环,那么只要计算出一组新的非负权重值,再使用Dijkstra;如果包含负环,则返回-1
3、重新计算权值:
如果仅仅是对每个边进行简单的加法,那么会改变该条的最短路径,这样的方法会导致经过边数多的路径加上更多的边权
方法:寻找一个新结点s(编号为0),使s于其他结点的最初路径是0,然后计算出s到每个结点的最短路径(由于有负值,一定有一部分0变为负值),把最短路径保存在h数组中,然后利用如下公式重新赋予边权,得到正边权
struct edge
{
int v, w, next;
} e[10005];
struct node {
int dis, id;
friend bool operator<(const node& a, const node& b)
{
return a.dis > b.dis;
}
node(int d, int x)
{
dis = d, id = x;
}
};
int head[maxn], vis[maxn], t[maxn];
int cnt, n, m;
int h[maxn], dis[maxn][maxn]; //如果仅同时使用一个i对应的dis可以开一维
void addedge(int u, int v, int w)
{
e[++cnt].v = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt;
}
int spfa(int s)
{
queue<int> q;
memset(h, 63, sizeof(h));
h[s] = 0, vis[s] = 1;
q.push(s);
while (!q.empty())
{
int u = q.front();
q.pop();
vis[u] = 0;
for (int i = head[u]; i; i = e[i].next)
{
int v = e[i].v;
if (h[v] > h[u] + e[i].w)
{
h[v] = h[u] + e[i].w;
if (!vis[v])
{
vis[v] = 1;
q.push(v);
t[v]++;
if (t[v] == n + 1)
return 0;
}
}
}
}
return 1;
}
void dijkstra(int s)
{
priority_queue<node> q;
for (int i = 1; i <= n; i++)
dis[s][i] = inf;
memset(vis, 0, sizeof(vis));
dis[s][s] = 0;
q.push(node(0, s));
while (!q.empty())
{
int u = q.top().id;
q.pop();
if (vis[u])
continue;
vis[u] = 1;
for (int i = head[u]; i; i = e[i].next)
{
int v = e[i].v;
if (dis[s][v] > dis[s][u] + e[i].w)
{
dis[s][v] = dis[s][u] + e[i].w;
if (!vis[v])
q.push(node(dis[s][v], v));
}
}
}
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int u, v, w;
cin >> u >> v >> w;
addedge(u, v, w);
}
for (int i = 1; i <= n; i++)
addedge(0, i, 0);
if (!spfa(0))
{
cout << -1 << endl;
return;
}
for (int u = 1; u <= n; u++)
for (int i = head[u]; i; i = e[i].next)
e[i].w += h[u] - h[e[i].v];
for (int i = 1; i <= n; i++)
{
dijkstra(i);
for (int j = 1; j <= n; j++)
{
dis[i][j] += h[j] - h[i];
}
}
}