• 图论学习笔记3


    Bellman-Ford 算法

    Bellman-Ford算法:
    DijkstraDijkstra类似,都以松弛操作为基础,即估计的最短路径值渐渐地被更加准确的值替代,直至得到最优解。在两个算法中,计算时每个边之间的估计距离值都比真实值大,并且被新找到路径的最小长度替代。

    松弛

    每次松弛操作实际上是对相邻节点的访问,第 n次松弛操作保证了所有深度为n的路径最短。由于图的最短路径最长不会经过超过 |V|-1条边。

    负边权操作

    “松弛”操作是在广度上找路,所以对负边进行操作而不会影响结果。

    负权环判定

    在处理完所有的边后,如果还有边可以松弛,那么就一定存在负权环。

    朴素实现

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int MAXN = 505;
    
    struct Node {
        int u, v, w;
    } node[MAXN];
    
    int n, m, s, t;
    int dis[MAXN], pre[MAXN];
    
    bool Bellman_Ford(int s, int t) {
        memset(dis, 0x3f, sizeof(dis));
        dis[s] = 0;
        pre[s] = 0;
        for (int i = 1; i < n; i++) {//迭代 n - 1次
            for (int j = 1; j <= m; j++) {//遍历每一条边
                if (dis[node[j].u] + node[j].w < dis[node[j].v]) {//松弛
                    dis[node[j].v] = dis[node[j].u] + node[j].w;
                    pre[node[j].v] = node[j].u;
                }
            }
        }
        for (int i = 1; i <= m; i++) {//判断负权环
            if (dis[node[i].u] + node[i].w < dis[node[i].v]) {
                return false;
            }
        }
        return true;
    }
    
    void print(int x) {//输出路径
        if (pre[x] == 0) {
            printf("%d ", x);
            return;
        }
        print(pre[x]);
        printf("%d ", x);
    }
    
    int main() {
        scanf("%d %d", &n, &m);
        for (int i = 1, ui, vi, wi; i <= m; i++) {
            scanf("%d %d %d", &ui, &vi, &wi);
            node[i].u = ui;
            node[i].v = vi;
            node[i].w = wi;
            pre[vi] = ui;
        }
        scanf("%d %d", &s, &t);
        if (Bellman_Ford(s, t)) {
            printf("%d
    ", dis[t]);
            print(t);
        } else {
            printf("No Solution
    ");
        }
        return 0;
    }
    

    Spfa

    思想

    建一个队列 ,取队头顶点u;将与点u相连的所有点v松弛,如果能更新估计值,那么就更新,如果点v没有在队列中,那么要将点v入队,如果已经在队列中了,那么就不用,循环直到队空为止,完成了单源最短路的求解。

    实现

    
    int n, m, s, t;
    int dis[MAXN];
    bool vis[MAXN];
    
    struct edge {//存边
        int v, w;
        edge() {}
        edge(int V, int W) {
            v = V;
            w = W;
        }
    };
    
    vector<edge> G[MAXM];
    queue<int> q;
    
    void AddEdge(int u, int v, int w) {//加边
        G[u].push_back(edge(v, w));
        G[v].push_back(edge(u, w));
    }
    
    void Spfa(int s, int t) {
        memset(dis, 0x3f, sizeof(dis));
        memset(vis, 0, sizeof(vis));
        dis[s] = 0;
        vis[s] = 1;
        q.push(s);
        while (q.size()) {
            int u = q.front();//取出队头
            q.pop();
            vis[u] = 0;
            for (int i = 0; i < G[u].size(); i++) {
                if (dis[u] + G[u][i].w < dis[G[u][i].v]) {//扩展新节点
                    dis[G[u][i].v] = dis[u] + G[u][i].w;
                    if (!vis[G[u][i].v]) {
                        vis[G[u][i].v] = 1;
                        q.push(G[u][i].v);
                    }
                }
            }
        }
    }
    
  • 相关阅读:
    移位运算符的限制
    数据类型
    正无穷和负无穷的问题
    dos下的cd指令
    c#线程的几种启动方法
    储过程实现简单的数据分页
    java 身份证15位转18位
    eclipse配置文件内存设置
    markdown语法学习
    js工具类
  • 原文地址:https://www.cnblogs.com/cqbz-ChenJiage/p/13504655.html
Copyright © 2020-2023  润新知