算法 -- 04 -- 图论-最短路问题

211 阅读1分钟

dijkstra算法

用于图论中求解最短路问题

思想是枚举距离初始点最近的点,并且把这个点相关的边更新,通过枚举n - 1次,我们就可以枚举出到达n的最短路径

不要忘记初始化dist[1] = 0

AcWing 849. Dijkstra求最短路 I - AcWing

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 500 + 10;
​
int n, m;
int g[N][N];
int dist[N]; //每个点到初始位置(1)的距离
bool st[N];
​
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    for(int i = 0; i < n - 1; i++) //
    {
        int t  = -1;
        for(int j = 1; j <= n; j++) //从i ~ n 中选出距离初始位置最近的点
        {
            if(!st[j] && (t == -1 || dist[j] < dist[t]))
            {
                t = j;
            }
        }
        
        for(int j = 1; j <= n; j++)
        {   //用t(距离起点最短)更新其他点的距离
            dist[j] = min(dist[j],dist[t] + g[t][j]);
        }
​
        st[t] = true;
    }
​
    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
​
int main()
{
    cin>>n>>m;
    memset(g, 0x3f, sizeof g);
    for(int i = 0; i < m; i++)
    {      
        int a, b, c;
        cin>>a>>b>>c;
        g[a][b] = min(g[a][b], c); //重边
    }
    cout<<dijkstra()<<endl;
}

Bellman-ford

思想:枚举一条边,并且更新这条边的dist, 循环k次,这样每次可行方案的边数是按循环次数来的。

但是要注意串联,当a 与 b 两条边相邻时,前面一条边a更新后,后面的b更新时会收到dist[a]的影响,这是不符合思想的,所以每次循环需要备份上一次dist的状态

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 500 + 10, M = 1e5 + 10;
int n, m, k;
int dist[N], backup[N];
struct Edge{
    int a;
    int b;
    int c;
} edges[M];
​
void bellman_ford()
{
    //将这距离初始化为正无穷
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    for(int i = 0; i < k; i++)
    {
        //避免串联,也就是前面的点更新使后面的也更新了
        memcpy(backup, dist, sizeof dist);
        //对边进行循环
        for(int j = 0; j < m; j++)
        {
            auto t = edges[j];
            //这里注意要与当前边终点的最短距离,
            //与该边未更新前的的最短距离加权值进行比较
            dist[t.b] = min(dist[t.b], backup[t.a]+t.c);
        }
    }
}
​
int main()
{
    cin>>n>>m>>k;
    for(int i = 0; i < m; i++)
    {
        int a, b, c;
        cin>>a>>b>>c;
        edges[i] = {a, b, c}; //这里不必处理重边,因为每次选择最小的边
    }
    
    bellman_ford();
    
    if(dist[n] > 0x3f3f3f3f / 2) puts("impossible");
    else cout<<dist[n]<<endl;
    return 0;
}

以上是个人觉得常用算法

下面是ACwing上大佬更精细的总结

最短路小总结-----图论小知识 - AcWing

\