• 图论 Algorithms


    1) Dijkstra

    基本思路:更新每个点到原点的最短路径;寻找最短路径点进行下一次循环;循环次数达到 n - 1 次说明每个点到原点的最短路已成,停止程序。

     1  function Dijkstra(Graph, source):
     2
     3      create vertex set Q
     4
     5      for each vertex v in Graph:             // Initialization
     6          dist[v] ← INFINITY                  // Unknown distance from source to v
     7          prev[v] ← UNDEFINED                 // Previous node in optimal path from source
     8          add v to Q                          // All nodes initially in Q (unvisited nodes)
     9
    10      dist[source] ← 0                        // Distance from source to source
    11      
    12      while Q is not empty:
    13          u ← vertex in Q with min dist[u]    // Source node will be selected first
    14          remove u from Q 
    15          
    16          for each neighbor v of u:           // where v is still in Q.
    17              alt ← dist[u] + length(u, v)
    18              if alt < dist[v]:               // A shorter path to v has been found
    19                  dist[v] ← alt 
    20                  prev[v] ← u 
    21
    22      return dist[], prev[]
    
    // Dijkstra
    #include <bits/stdc++.h>
    using namespace std;
    const int N = 25000 + 5, M = 62000 + 5;
    const int inf = 2147483647;
    int n, m, s, e, dis[N];
    int first[N], nex[M], to[M], w[M], en;
    bool v[N];
    
    void add(int x, int y, int z) {
        nex[++en] = first[x]; first[x] = en; to[en] = y; w[en] = z;
        nex[++en] = first[y]; first[y] = en; to[en] = x; w[en] = z;
    }
    
    int main() {
        int x, y, z;
        scanf("%d%d%d%d", &n, &m, &s, &e);
        for (int i = 1; i <= n; i++)
            dis[i] = inf;
        for (int i = 1; i <= m; i++) {
            scanf("%d%d%d", &x, &y, &z);
            add(x, y, z);
        }
    
        dis[s] = 0; v[s] = true;
        int k = s;
        for (int i = 1; i <= n - 1; i++) {
            int minlen = inf;
            for (int j = first[k]; j; j = nex[j])
                if (dis[k] + w[j] < dis[to[j]])
                    dis[to[j]] = dis[k] + w[j];
            for (int j = 1; j <= n; j++) {
                if ((!v[j]) && (dis[j] < minlen)) {
                    k = j;
                    minlen = dis[j];
                }
            }
            v[k] = true;
        }
    
        printf("%d
    ", dis[e]);
        return 0;
    }
    
    /* Dijkstra Optimized
     * Au: H15teve
     */
    #include <bits/stdc++.h>
    // header <queue> included.
    using namespace std;
    
    const int N = 200003, M = 1000003, inf = 2147483647;
    
    struct node {
    	int u, dis;
    	bool operator < (const node &n) const {
    		return dis > n.dis;
    	}
    } temp;
    
    priority_queue<node> q;
    
    int d[N], n, m, s, head[N], nex[M], to[M], w[M], en;
    bool v[N];
    
    inline void add(int x, int y, int z) {
    	nex[++en] = head[x], head[x] = en,
    	to[en] = y, w[en] = z;
    }
    
    int main() {
    	scanf("%d%d%d", &n, &m, &s);
    	for (int i = 1; i <= n; i++) d[i] = inf;
    	for (int i = 1, x, y, z; i <= m; i++) {
    		scanf("%d%d%d", &x, &y, &z);
    		add(x, y, z);
    	}
    
    	d[s] = 0, temp.u = s; q.push(temp);
    
    	while (!q.empty()) {
    		temp = q.top(), q.pop();
    		if (v[temp.u]) continue;
    		v[temp.u] = true;
    
    		for (int i = head[temp.u]; i; i = nex[i])
    			if (temp.dis + w[i] < d[to[i]])
    				d[to[i]] = temp.dis + w[i],
    				q.push((node) {to[i], d[to[i]]});
    	}
    
    	for (int i = 1; i <= n; i++)
    		printf("%d ", d[i]);
    	return 0;
    }
    

    2) Kruskal

    基本思路:按边长度从小到大排序,循环添加「不成环」的边;边数达到 n - 1 说明最小生成树已成,停止程序。

    /* Kruskal
     * Au: GG
     */
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <ctime>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int N = 1000 + 3, M = 20000 + 3;
    int dset[N], n, m;
    
    struct edge {
        int x, y, w;
        bool operator < (const edge &a) const { return w < a.w; }
    } edges[M];
    
    int find(int x) { return (dset[x] == -1) ? x : dset[x] = find(dset[x]); }
    void join(int x, int y) { if (find(x) != find(y)) dset[find(x)] == find(y); }
    
    int Kruskal() {
        memset(dset, -1, sizeof(dset));
        sort(edges + 1, edges + m + 1);
        int cnt = 0, tot = 0;
        for (int i = 1; i <= m; i++)      //循环所有已从小到大排序的边
            if (find(edges[i].x) != find(edges[i].y)) { // (因为已经排序,所以必为最小)
                join(edges[i].x, edges[i].y); // 相当于把边(u,v)加入最小生成树。
                tot += edges[i].w;
                cnt++;
                if (cnt == n - 1) // 说明最小生成树已经生成
                    break; 
            }
        return tot;
    }
    
    int main() {
        printf("Enter vertex number & edge number:
    ");
        scanf("%d%d", &n, &m);
        printf("Enter information of %d edge(s):
    ", m);
        for (int i = 1; i <= m; i++) {
            scanf("%d%d%d", &edges[i].x, &edges[i].y, &edges[i].w);
        }
    
        printf("The weight of minimum spanning tree is %d.
    ", Kruskal());
        
        return 0;
    }
    

    3) Bellman-Ford

    基本思路:更新每个点到原点的最短路径;循环次数达到 n - 1 次说明每个点到原点的最短路已成,停止程序。

    // Bellman-Ford
    // Au: GG
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 25000 + 5, maxm = 62000 + 5;
    const int inf = 1000000001;
    int n, m, s, e, en;
    int from[maxm], to[maxm], w[maxm];
    int dis[maxn], pre[maxm];
    void add(int x, int y, int z) {
        from[++en] = x; to[en] = y; w[en] = z;
    }
    int main() {
        int x, y, z;
        scanf("%d%d%d%d", &n, &m, &s, &e);
        for (int i = 1; i <= n; i++)
            dis[i] = inf;
        for (int i = 1; i <= m; i++) {
            scanf("%d%d%d", &x, &y, &z);
            add(x, y, z);
        }
        dis[s] = 0; pre[s] = 0;
        for (int i = 1; i <= n - 1; i++)
            for (int j = 1; j <= m; j++)
                if (dis[from[j]] + w[j] < dis[to[j]]) {
                    dis[to[j]] = dis[from[j]] + w[j];
                    pre[to[j]] = from[j];
                }
        printf("%d
    ", dis[e]);
        return 0;
    }
    

    4) Floyd

    基本思路:枚举所有点与点的中点,如果从中点走最短,更新两点间距离值。

    procedure Floyd–Warshall(G)
    1 let dist be a |V| × |V| array of minimum distances initialized to ∞ (infinity)
    2 for each vertex v
    3    dist[v][v] ← 0
    4 for each edge (u,v)
    5    dist[u][v] ← w(u,v)  // the weight of the edge (u,v)
    6 for k from 1 to |V|
    7    for i from 1 to |V|
    8       for j from 1 to |V|
    9          if dist[i][j] > dist[i][k] + dist[k][j] 
    10             dist[i][j] ← dist[i][k] + dist[k][j]
    11         end if
    
    // Floyd
    // Au: GG
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1000 + 3;
    const int inf = 1000000001;
    int n, m;
    int f[maxn][maxn];
    int main() {
        scanf("%d%d", &n, &m);
        int i, j, k, a, b, w;
        for (i = 1; i <= n; i++)
            for (j = 1; j <= n; j++)
                f[i][j] = inf;
        for (i = 1; i <= m; i++) {
            scanf("%d%d%d", &a, &b, &w);
            if (f[a][b] > w) f[a][b] = w, f[b][a] = w;
        }
        for (k = 1; k <= n; k++)
            for (i = 1; i <= n; i++)
                for (j = 1; j <= n; j++)
                    if (f[i][j] > f[i][k] + f[k][j])
                        f[i][j] = f[i][k] + f[k][j];
        scanf("%d%d", &a, &b);
        printf("%d", f[a][b]);
        return 0;
    }
    

    5) SPFA

    基本思路:更新每个点到原点的最短路径,保证「路径可变得更小的点」在队列中;队列空说明每个点到原点的最短路已成,停止程序。

    procedure Shortest-Path-Faster-Algorithm(G, s)
      1    for each vertex v ≠ s in V(G)
      2        d(v) := ∞
      3    d(s) := 0
      4    offer s into Q
      5    while Q is not empty
      6        u := poll Q
      7        for each edge (u, v) in E(G)
      8            if d(u) + w(u, v) < d(v) then
      9                d(v) := d(u) + w(u, v)
     10                if v is not in Q then
     11                    offer v into Q
    
    /* Shortest Path Faster Algorithm
     * Au: GG
     */
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int N = 1000 + 3, M = 10000 + 3;
    int n, m, s, d[N], pre[N], enq[N];
    bool inq[N];
    int head[N], nex[M], to[M], w[N], en;
    queue<int> q;
    
    void add(int x, int y, int z) {
        nex[++en] = head[x]; head[x] = en; to[en] = y; w[en] = z;
        nex[++en] = head[y]; head[y] = en; to[en] = x; w[en] = z;
    }
    
    bool SPFA() {
        memset(d, 0x7f, sizeof(d));
        q.push(s); d[s] = 0; inq[s] = true; enq[s]++;
    
        while (!q.empty()) {
            int a = q.front(); q.pop();
            inq[a] = false;
            for (int b = head[a]; b; b = nex[b]) 
                if (d[a] + w[b] < d[to[b]]) {
                    d[to[b]] = d[a] + w[b];
                    pre[to[b]] = a;
                    if (!inq[to[b]]) {
                        q.push(to[b]);
                        enq[to[b]]++; if (enq[to[b]] >= n) return false;
                        inq[to[b]] = true;
                    }
                }
        }
        return true;
    }
    
    int main() {
        int a, b, c;
        printf("Enter vertex number, edge number & source point index:
    ");
        scanf("%d%d%d", &n, &m, &s);
        printf("Enter information of %d edge(s):
    ", m);
        for (int i = 1; i <= m; i++) {
            scanf("%d%d%d", &a, &b, &c);
            add(a, b, c);
        }
    
        if (SPFA()) {
            for (int i = 1; i <= n; i++)
                if (i != s) {
                    printf("Distance between source point to point %d is %d.
    ", i, d[i]);
                    int p = i;
                    stack<int> ans;
                    printf("    Path: %d", s);
                    while (s != p) ans.push(p), p = pre[p];
                    while (!ans.empty()) printf(" -> %d", ans.top()), ans.pop();
                    printf("
    ");
                }
        } else printf("The graph has negative circle!
    ");
        
        return 0;
    }
    

    「重拾最短路」可参考 61mon.com 的博客:

    1. Dijkstra (Link link)
    2. Bellman-Ford (Link link)
    3. SPFA (Link link)
    4. 总结 (Link link)

    Reference

    1. internal (blog/83) by SJoshua
    2. Stack Overflow. Trying to understand Dijkstra's Algorithm[DB/OL]. Link link, 2017-08-04

    Post author 作者: Grey
    Copyright Notice 版权说明: Except where otherwise noted, all content of this blog is licensed under a CC BY-NC-SA 4.0 International license. 除非另有说明,本博客上的所有文章均受 知识共享署名 - 非商业性使用 - 相同方式共享 4.0 国际许可协议 保护。
  • 相关阅读:
    NumPy笔记:运算符(exp,sqrt,square)
    NumPy笔记:常用操作
    php字符操作
    laravel如何实现批量插入
    php中@符号的作用
    laravel如何实现数据的批量插入
    如何在laravel框架中使用阿里云的oss
    laravle如何设置mysql数据表前缀
    thinkphp视图中如何调用并且向控制器传参数
    thinkphp如何隐藏入口文件index.php
  • 原文地址:https://www.cnblogs.com/greyqz/p/7297505.html
Copyright © 2020-2023  润新知