图论——最短路径 Dijkstra算法、Floyd算法

1.弗洛伊德算法(Floyd)

弗洛伊算法核心就是三重循环,M [ j ] [ k ] 表示从 j 到 k 的路径,而 i 表示当前 j 到 k 可以借助的点;红色部分表示,如果 j 到 i ,i 到 k 是通的,就将 j 到 k 的值更新为

M[j][i] + M[i][k] 和 M[j][k] 较短的一个。
const int inf = 1<<30;

for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            for (int k = 1; k <= n; k++) {
                if (j!=k) {
                        M[j][k] = min(M[j][i] + M[i][k] , M[j][k]);
                }
            }
        }
}

给个题目链接,写完可以交试一下:http://www.dotcpp.com/oj/problem1709.html

完整代码:

#include <iostream>
#include <queue>
using namespace std;


#define inf 2147483647
int M[1000][1000];


int main() {
    int n;
    queue<int>q;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> M[i][j];
            if (M[i][j] == 0 && i != j)M[i][j] = inf;
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            for (int k = 1; k <= n; k++) {
                if (M[j][k] != 0) {
                    if (M[j][i] != inf && M[i][k] != inf) {
                        M[j][k] = M[j][i] + M[i][k] < M[j][k] ? M[j][i] + M[i][k] : M[j][k];
                    }
                }
            }
        }
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (M[i][j] == inf)cout << -1 << " ";
            else
            cout << M[i][j] << " ";
        }
        cout << endl;
    }

    return 0;
}
View Code

2.迪杰斯特拉

Floyd只要暴力的三个for就可以出来,代码很好理解,但缺点就是时间复杂度高是O(n³)。Dijkstra的时间复杂度是O(n²),要快很多。

不过要注意这个算法所求的是单源最短路。所以说,如果题目是求任意一对顶点间的最短路径问题,那就需要对每个顶点进行一遍迪杰斯特拉算法,这种情况就适合弗洛伊德算法了。

思想图解:

 用dis数组实时记录起始点(起始点取1) 到达的所有节点的距离。自己到自己的路径长度 0,到不了的点是 inf(极大值))

dis数组初始值是这样的,4是当前距离节点1最近的点。(已经访问过的,我们标记上不再次访问)

借助4节点,对dis数组进行更新(如果有更短的路径,就对dis数组进行值替换),走到2,无操作。

借助3节点,对dis数组进行更新,最后走到5节点,退出。(实际过程中,走到最后一个节点,别的节点都访问过,进行标记了,什么也不会做)。

 

借助3节点,对dis数组进行更新

测试题目:http://acm.hdu.edu.cn/showproblem.php?pid=2544 (数据很弱,AC了,实现也不一定是正确的,强烈建议再做后面一题)

#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;

const int inf = 1 << 30;
int n, m;
bool book[1001];
int M[1001][1001];
int dis[1001];

void initialize() {
    memset(book, 0, sizeof(book));
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (i != j)M[i][j] = inf;
        }
    }
}
void dijkstra() {
    while (true) {
        int v = 0;
        for (int i = 1; i <= n; i++) {
            if (!book[i] && (v == 0 || dis[i] < dis[v])) v = i;//从dis数组中找出当前距离起点最短的节点
        }
        if (v == 0) break;
        book[v] = true;
        for (int i = 1; i <= n; i++) {
            dis[i] = min(dis[i], dis[v] + M[v][i]);
        }
    }
}
int main() {
    while (cin >> n >> m) {
        if (n == 0 && m == 0)break;
        initialize();
        for (int i = 0; i < m; i++) {
            int A, B, C;
            cin >> A >> B >> C;
            M[A][B] = C;
            M[B][A] = C;
        }
        book[1] = true;
        for (int i = 1; i <= n; i++) {
            dis[i] = M[1][i];
        }
        dijkstra();

        cout << dis[n] << endl;
    }
    return 0;
}
View Code

这题数据很强,https://www.luogu.org/problemnew/show/P4779 数据量很大,100000个节点,不管是时间还是空间上,不能再用邻接矩阵了

邻接表+优先级队列:

#include <iostream>
#include <stdio.h>
#include <queue>
using namespace std;
int dis[100001];
bool fuck[100001];
const int inf = 1 << 30;

class ENode {
public:
    int to;
    int dis;
    ENode *next = NULL;

    void push(int t, int d) {
        ENode *p = new ENode;;
        p->to = t; p->dis = d;
        p->next = next;
        next = p;
    }

    bool operator<(ENode e)const {
        return e.dis < dis;
    }
}head[100001];

int main() {
    priority_queue<ENode>q;
    int n, m, s, c1, c2, c3;
    cin >> n >> m >> s;
    for (int i = 0; i < m; i++) {
        //cin >> c1 >> c2 >> c3;
        scanf("%d%d%d", &c1, &c2, &c3);
        head[c1].push(c2, c3);
    }

    for (int i = 1; i <= n; i++) {
        if (i != s) {
            dis[i] = inf;
        }
    }

    ENode *p = head[s].next;
    while (p) {
        if (p->dis == 0 || (p->dis < dis[p->to])) {
            q.push(*p);
            dis[p->to] = p->dis;
        }
        p = p->next;
    }
    
    while (!q.empty()) {
        //获得当期距离 源点 最近的点
        int min = q.top().to; q.pop();
        if (fuck[min])continue;
        fuck[min] = true;
        ENode *p = head[min].next;
        while (p) {
            int to = p->to;
            if (dis[to] > dis[min] + p->dis) {
                dis[to] = dis[min] + p->dis;
                ENode e = *p;
                e.dis = dis[to];
                q.push(e);
            }
            p = p->next;
        }
    }

    for (int i = 1; i <= n; i++) {
        printf("%d ", dis[i]);
    }
    cout << endl;
    return 0;
}
View Code

3、Bellman-Ford算法

Bellman - ford效率较低,代码难度较小。重要的是若给定的图存在负权边,Dijkstra算法便没有了用武之地,Bellman - ford算法便派上用场了。

 

转载于:https://www.cnblogs.com/czc1999/p/10385647.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值