zoukankan      html  css  js  c++  java
  • 单源最短路径 Bellman_ford 和 dijkstra

    首先两个算法都是常用于 求单源最短路径

    关键部分就在于松弛操作 实际上就是dp的感觉

    if (dist[e.to] > dist[v] + e.cost)

    {

      dist[e.to] = dist[v] + e.cost;  

      ...

    }

    bellman_ford O(E*V) 但是配合队列可以 有spfa 可以达到O(kE) 

    http://www.360doc.com/content/13/1208/22/14357424_335569176.shtml

    并且bellman_ford还适用于负边 并且可以利用最多循环V-1次的性质 判断是否存在负圈(如果循环超过V-1次 说明有负圈 因为没重复走着负圈一次 都伴随着更新)

    并且因为松弛操作是针对于每条边的遍历也可以用向前星存

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <fstream>
     4 #include <string.h>
     5 #define MAXV 10007
     6 #define MAXE 1007
     7 #define INF 0x3f3f3f3f
     8 using namespace std;
     9 
    10 int V, E;
    11 
    12 struct Edge
    13 {
    14     int from, to, cost;
    15 }edge[MAXE];
    16 
    17 
    18 
    19 
    20 int bellman_ford(int s, int d)//求单源最短路径
    21 {
    22     int dist[MAXV];
    23     fill (dist, dist+V+10, INF);
    24     dist[s] = 0;
    25     while (true)//直至update不在更新
    26     {
    27         bool update = false;
    28         for (int i = 0; i < E; i++)
    29         {
    30             if (dist[edge[i].from] != INF && dist[edge[i].to] > dist[edge[i].from] + edge[i].cost)
    31             {
    32                 dist[edge[i].to] = dist[edge[i].from] + edge[i].cost;
    33                 update = true;
    34             }
    35         }
    36         if (!update) break;
    37     }
    38     return dist[d];
    39 }
    40 //while(true) 循环中最多循环V-1次 复杂度是O(V*E)
    41 //如果存在 从s可达的负圈 那么在第V次循环时也会更新 可以用这个性质来检查负圈
    42 //如果一开始把dist都初始化为0  那么可以检查出所有的负圈
    43 //检查负边 是负边 返回true
    44 //P.S 圈表示基本回路
    45 bool isNegtive()
    46 {
    47     int dist[MAXV];
    48     memset(dist, 0, sizeof(dist));//单纯查负圈
    49     for (int i = 0; i < V; i++)
    50     {
    51         for (int i = 0; i < E; i++)
    52         {
    53             Edge e = edge[i];
    54             if (dist[e.to] < dist[e.from] + e.cost )
    55             {
    56                 dist[e.to] = dist[e.from] + e.cost;
    57                 if (i == V-1)//如果第V次还更新 那么就有负圈
    58                     return true;
    59             }
    60         }
    61     }
    62     return false;
    63 }
    64 int main()
    65 {
    66     ifstream cin ("in.txt");
    67     cin >> V >> E;
    68     for (int i = 0; i < E; i++)
    69     {
    70         cin >> edge[i].from >> edge[i].to >> edge[i].cost;
    71     }
    72     cout << bellman_ford(1, 7) << endl;
    73 }

    dijkstra 普通写法O(V^2)

    但是 dijkstra的特点 --->>是针对每一个点 通过它所对应的边更新 e.to的节点 

    每次取出的是离原点sorce 最近的点 所以就可以使用优先队列 那么就变成O(V*logV)

    并且因为“是针对每一个点 通过它所对应的边更新 e.to的节点 ” 那么也很好试用向前星

      1 #include <iostream>
      2 #include <string.h>
      3 #include <fstream>
      4 #include <stdio.h>
      5 #include <queue>
      6 #define MAXV 10007
      7 #define MAXE 1007
      8 #define INF 0x3f3f3f3f
      9 using namespace std;
     10 
     11 int V, E;
     12 //迪杰特斯拉好像不是很好用向前星
     13 //这个算法是针对每一个点更新 而向前星存储的是边的信息 要查找点就比较复杂
     14 
     15 //用邻接矩阵存储 O(V^2)的算法
     16 
     17 int cost[MAXV][MAXV];
     18 int dijkstra(int s, int d)
     19 {
     20     int dist[MAXV];
     21     bool use[MAXV];
     22     fill(dist, dist+MAXV, INF);
     23     memset(use, false, sizeof(use));
     24     dist[s] = 0;
     25     while (true)
     26     {
     27         int v = -1;
     28         for (int i = 1; i <= V; i++)//找一个最近的 未使用过的点
     29         {
     30             if (!use[i] && (v == -1 || dist[i] < dist[v])) v = i;
     31         }
     32         if (v == -1) break;
     33         use[v] = true;
     34         for (int i = 1; i <= V; i++)
     35         {
     36             dist[i] = min(dist[i], dist[v] + cost[v][i]);
     37         }
     38     }
     39     return dist[d];
     40 }
     41 
     42 //使用优先队列(也就是heap优化)O(E*logV)
     43 //针对边来dp 这里可以用向前星存储了
     44 struct Edge
     45 {
     46     int to, n, next;
     47 }edge[MAXE];
     48 int num = 0;
     49 int head[MAXV];
     50 void Add(int from, int to, int c)
     51 {
     52     edge[num].n = c;
     53     edge[num].to = to;
     54     edge[num].next = head[from];//从头插入
     55     head[from] = num++;
     56 }
     57 void init()//初始化
     58 {
     59     ifstream cin("in.txt");
     60     memset(edge, -1, sizeof(edge));
     61     memset(head, -1, sizeof(head));
     62     cin >> V >> E;
     63     for (int j = 0; j < E; j++)
     64     {
     65         int from , to , c;
     66         cin >> from >> to >> c;
     67         Add(from, to, c);
     68     }
     69 }
     70 
     71 typedef pair<int, int> P;//first 表示与s的距离, second表示点的编号
     72 int pre[MAXV];//记录前驱节点 在每次dist[j] = dist[k] + cost[k][j]更新时记录在到j的最短路径上的前驱节点就是k
     73 int fast_dijkstra(int s, int d)
     74 {
     75     int dist[MAXV];
     76     priority_queue<P, vector<P>, greater<P> > que;//用优先队列存储点的信息 每次弹出距离最短的点O(logV)
     77     fill(dist, dist+MAXV, INF);
     78     dist[s] = 0;
     79     que.push(P(0, s));
     80     while (!que.empty())
     81     {
     82         P p = que.top();
     83         que.pop();
     84         if (dist[p.second] < p.first) continue;
     85         int t = head[p.second];
     86         while (t != -1)
     87         {
     88             Edge e = edge[t];
     89             if (dist[e.to] > dist[p.second] + e.n)
     90             {
     91                 dist[e.to] = dist[p.second] + e.n;
     92                 pre[e.to] = p.second;
     93                 que.push( P(dist[e.to], e.to) );
     94             }
     95             t = e.next;
     96         }
     97     }
     98     return dist[d];
     99 }
    100 
    101 int main()
    102 {
    103     /*
    104     ifstream cin("in.txt");
    105     cin >> V >> E;
    106     for (int i = 1; i <= V; i++)
    107         for (int j = 1; j <= V; j++)
    108             cost[i][j] = INF;//不存在时 cost为INF
    109     for (int j = 0; j < E; j++)
    110     {
    111         int from , to , c;
    112         cin >> from >> to >> c;
    113         cost[from][to] = c;
    114     }
    115     int ans = dijkstra(1, 7);
    116     */
    117     init();
    118     int ans = fast_dijkstra(1,7);
    119     cout << ans << endl;
    120     //这样路径即还原了
    121     int t = pre[7];
    122     cout << 7 << " ";
    123     while(t != 1)
    124     {
    125         cout << t << " ";
    126         t = pre[t];
    127     }
    128     cout << 1 <<endl;
    129 }
  • 相关阅读:
    教你如何把切割好的html页面转换成可供DIY的页面
    [二次开发][Discuz!X] Discuz!二次开发基本知识
    discuz之C::t 方法
    header_userstatus.htm文件注释
    【discuzx2】header.htm模板文件深入分析
    discuz的页头header和页尾footer文件必要代码
    discuz全局数组变量 后台各项设置 完整版
    交换机到底有没有MAC地址?
    妙味课堂—JavaScript基础课程笔记
    jquery 绘图工具 flot 学习笔记
  • 原文地址:https://www.cnblogs.com/oscar-cnblogs/p/6399594.html
Copyright © 2011-2022 走看看