zoukankan      html  css  js  c++  java
  • 康复计划之最短路

    最短路问题:给你一张图(n个点,m条边),每条边有一个距离。问从一个点到另一个点的最短距离。

    最短路主要关注两种算法: Dijkstra  O(n^2) 和 SPFA O(n*m)  (都是最坏复杂度) (其实SPFA几乎无法达到最坏复杂度)

    Dijkstra:

    适用范围(局限性):不存在负权边

    思路:

    我们取两个集合,一个集合M中为已确定最短距离的点集,另一个集合N为还没有确定最短距离的点集。

    我们用集合M中的所有点去更新N中的点的距离,那么集合N中距离最小的点就可以确定该距离为最小距离了。

    最初集合M中只有出发点,逐渐向外扩展。

    对于思路中的第二行,我们予以证明:

    假设用集合M中的点更新N中的点的距离后,N中的最小距离点o不能被确定为最短路径,那么N中必须存在一个点p,使得M->p->o为o点最短路径,即s(M->p->o) < s(M->o)。而s(M->p) > s(M->o) 且 s(p->o) > 0(不存在负权边),所以s(M->p->o) < s(M->o)不成立。推出矛盾。

    故第二行得证。

    然后我们考虑代码实现。

    最容易想到的做法为每往集合M中加入一个元素x,更新由x可扩展到的点的最短距离。然后遍历一遍所有点,找到位于集合N中且距离最小的点,把它加入集合M中...

    如此循环。确定复杂度O(n^2)

    我们考虑一个可以优化的部分:遍历一遍所有点,找到位于集合N中且距离最小的点,把它加入集合M中。

    我们只需要找到一个点,但我们遍历了所有点,这是不是没有必要呢?

    我们可以用一个小根堆,去储存集合N中的点的距离,那么我们每次寻找最小距离的点的复杂度就可以降低到logn。

    于是Dijkstra的复杂度就降低到了O(m+nlogn) 

    (其实未优化Dijkstra的复杂度为O(m+n^2),由于 m <= n^2,故写为O(n^2) )

    给个模板题:click here

    Dijkstra:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<queue>
     6 using namespace std ;
     7 const int N = 100000 + 10 ;
     8 const int M = 100000 + 10 ;
     9 const int INF = 0x7ffffff ;
    10 
    11 inline int read() {
    12     int k = 0, f = 1 ; char c = getchar() ;
    13     for( ; !isdigit(c) ; c = getchar())
    14       if(c == '-') f = -1 ;
    15     for( ; isdigit(c) ; c = getchar())
    16       k = k*10 + c-'0' ;
    17     return k*f ;
    18 }
    19 
    20 struct Edge {
    21     int to, nex, val ;
    22 }e[N] ;
    23 int n, m, cnt = 0 ;  int head[N], dis[N] ;  
    24 bool vis[N] ;
    25 
    26 struct HeapNode {
    27     int d, u ;
    28     bool operator <  (const HeapNode& rhs) const {
    29         return d > rhs.d ;
    30     }
    31 };
    32 priority_queue<HeapNode>q ;
    33 
    34 inline void add_edge(int x,int y,int z) {
    35     e[++cnt].nex = head[x] ; head[x] = cnt ; e[cnt].to = y ; e[cnt].val = z ;
    36 }
    37 
    38 inline void djsk() {
    39     dis[1] = 0 ;
    40     for(int i=2;i<=n;i++) dis[i] = INF ;
    41     memset(vis,0,sizeof(vis)) ;
    42     q.push((HeapNode){0,1}) ;
    43     while(!q.empty()) { 
    44         HeapNode xx = q.top() ; q.pop() ;
    45         int x = xx.u ;
    46         if(vis[x]) continue ;  vis[x] = 1 ;
    47         for(int i=head[x];i;i=e[i].nex) {
    48             int y = e[i].to ;  if(vis[y]) continue ;
    49             if(dis[y] > dis[x]+e[i].val) {
    50                 dis[y] = dis[x] + e[i].val ;
    51                 q.push((HeapNode){dis[y],y}) ;
    52             }
    53         }
    54     }
    55 }
    56 
    57 int main() {
    58     n = read(), m = read() ;
    59     for(int i=1;i<=m;i++) {
    60         int x, y, z ;  x = read(), y = read(), z = read() ;
    61         add_edge(x,y,z) ;
    62     }
    63     djsk() ;
    64     printf("%d
    ",dis[n]) ;
    65     return 0 ;
    66 }

    SPFA:

    适用范围:不存在负权环 (可以存在负权边) (可以检验图中是否存在负权环)

    其实SPFA是Bellman-Ford算法的优化,所以我们先介绍Bellman-Ford算法。

    Bellman-Ford:

    思路:

    采用bfs的方法一层一层往外扩展。

    每扩展一层,用该扩展层去更新更下面一层,并对于可更新的点,将其加入下一层要扩展的集合中。

    第零层为出发点。

    最坏复杂度:O(n*m)    (其实难以达到)

    SPFA:

    用一个先进先出队列储存可更新的点。

    每次从队首取出一个点x,用x去松弛其它点,遇到能松弛的点x,就将x加入队尾。

    直至队列为空。

    一个小优化:

    如果dis[y] < dis[q.front()] 就将其加入队首,而不是队尾。

    SPFA代码(双向队列优化):

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<queue>
     6 using namespace std ;
     7 const int N = 100000 + 10 ;
     8 const int M = 100000 + 10 ; 
     9 const int INF = 0x7ffffff ;
    10 
    11 inline int read() {
    12     int k = 0, f = 1 ; char c = getchar() ;
    13     for( ; !isdigit(c) ; c = getchar())
    14       if(c == '-') f = -1 ;
    15     for( ; isdigit(c) ; c = getchar())
    16       k = k*10 + c-'0' ;
    17     return k*f ;
    18 }
    19 
    20 struct Edge {
    21     int to, nex, cos ;
    22 }e[M] ;
    23 
    24 int n, m ;  int head[N], dis[N] ;
    25 bool inq[N] ;
    26 deque<int>q ;
    27 
    28 inline void add_edge(int x,int y,int z) {
    29     static int cnt = 0 ;
    30     e[++cnt].to = y, e[cnt].nex = head[x], head[x] = cnt, e[cnt].cos = z ;
    31 }
    32 
    33 int main() {
    34     int n, m ;
    35     n = read(), m = read() ;
    36     for(int i=1;i<=m;i++) {
    37         int x = read(), y = read(), z = read() ;
    38         add_edge(x,y,z) ;
    39     }
    40     for(int i=2;i<=n;i++) dis[i] = INF ;
    41     q.push_back(1) ; dis[1] = 0 ;
    42     while(!q.empty()) {
    43         int x = q.front() ; q.pop_front() ; inq[x] = 0 ;
    44         for(int i=head[x];i;i=e[i].nex) {
    45             int y = e[i].to ;
    46             if(dis[y] > dis[x]+e[i].cos) {
    47                 dis[y] = dis[x]+e[i].cos ;
    48                 if(!inq[y]) {
    49                     if(!q.empty() && dis[y] < dis[q.front()]) q.push_front(y) ;
    50                     else q.push_back(y) ;
    51                     inq[y] = 1 ;
    52                 }
    53             }
    54         }
    55     }
    56     printf("%d",dis[n]) ;
    57     return 0 ;
    58 }
  • 相关阅读:
    Soap1.1和Soap1.2的区别
    常用开源软件许可协议简介
    Web优化之Javascript Compressor
    Web优化之YaHoo Web优化的14条法则
    Installing Cygwin on Windows 7 And Configure SSH
    Different Between Cygwin And MinGw
    xml读取异常Invalid byte 1 of 1-byte UTF-8 sequence
    JAVA事务系列三:JTA事务
    JAVA事务系列二:JDBC事务
    帅才将才慧才
  • 原文地址:https://www.cnblogs.com/zub23333/p/11612965.html
Copyright © 2011-2022 走看看