<学习笔记> 最短路

floyd 算法

Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。(摘自baidu)

时间复杂度 : O (n^3); 

空间复杂度:O(n^2) ;

优缺点分析

       Floyd算法适用于APSP(All Pairs Shortest Paths,多源最短路径),是一种动态规划算法,稠密图效果最佳,边权可正可负。此算法简单有效,由于三重循环结构紧凑,对于稠密图,效率要高于执行|V|次Dijkstra算法,也要高于执行|V|次SPFA算法

 优点:容易理解,可以算出任意两个节点之间的最短距离,代码编写简单。

 缺点:时间复杂度比较高,不适合计算大量数据。(摘自 baidu)

 
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 
 6 int N,M,S,T,a,b,c;
 7 int de[510][510];
 8 
 9 int main()
10 {
11        scanf("%d%d%d%d",&N,&M,&S,&T);
12        memset(de,63,sizeof(de));
13        for(int i=1;i<=M;++i)
14        {
15            scanf("%d%d%d",&a,&b,&c);
16            de[a][b]=c;
17            de[b][a]=c;
18        }
19        for(int k=1;k<=N;++k) // 枚举中间点必须放在第一重循环
20           for(int i=1;i<=N;++i)
21              for(int j=1;j<=N;++j)
22                 if(i!=k&&j!=k&&i!=j)
23                     de[i][j]=min(de[i][j],de[i][k]+de[k][j]);
24        printf("%d",de[S][T]);
25        return 0;
26 } 

 

BFS

  求边权都为1的图的单源最短路径。

dijkstra 算法

迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。

求单源最短路径的算法。

采用贪心的思想,每次都用当前de最小的点来更新其他所有点,并把他标记为used(其他点都无法再更新他),直到所有点都不能更新为止。

复杂度 O(n^2)

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 
 6 int N,M,S,T,cnt,a,b,c;
 7 int Rode[2510][2510];
 8 int first[2510],next[13000],de[2510];
 9 bool used[2500];
10 
11 int main()
12 {
13      scanf("%d%d%d%d",&N,&M,&S,&T);
14      memset(de,63,sizeof(de));
15      memset(Rode,63,sizeof(Rode));
16      for(int i=1;i<=M;++i)
17      {
18           scanf("%d%d%d",&a,&b,&c);
19          Rode[a][b]=c;
20          Rode[b][a]=c;
21      }
22      de[S]=0;
23      while(true)
24      {
25            int maxn=1e9+7,pos=0;
26            for(int i=1;i<=N;++i)
27               if(!used[i]&&maxn>de[i])
28                   maxn=de[i],pos=i;
29            if(!pos) break;
30            used[pos]=1;
31            for(int i=1;i<=N;++i)
32               if(de[i]>de[pos]+Rode[pos][i])
33                   de[i]=de[pos]+Rode[pos][i];
34      }
35      printf("%d",de[T]);
36      return 0;
37 } 
 dijkstra 堆优化

每次找de最小的点时用堆。

时间复杂度 O((m+n)logn)

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<queue>
 5 using namespace std;
 6 
 7 int N,M,S,T,cnt,a,b,c;
 8 int first[2510],next[13000],de[2510];
 9 bool used[2500];
10 
11 struct maple{
12     int f,t,d;
13 }Rode[13000];
14 struct leaf{
15     int f,d;
16     bool operator <(const leaf &a) const{
17          return a.d<d;
18     } 
19 }; 
20 
21 void Build(int f,int t,int d)
22 {
23     Rode[++cnt]=(maple){f,t,d};
24     next[cnt]=first[f];
25     first[f]=cnt;
26 }
27 
28 priority_queue<leaf> q;
29 
30 int main()
31 {
32      scanf("%d%d%d%d",&N,&M,&S,&T);
33      memset(de,63,sizeof(de));
34      for(int i=1;i<=M;++i)
35      {
36           scanf("%d%d%d",&a,&b,&c);
37           Build(a,b,c);
38           Build(b,a,c);
39      }
40      de[S]=0;
41      q.push((leaf){S,0});
42      while(!q.empty())
43      {
44           leaf A=q.top();
45           q.pop();
46           while(!q.empty()&&used[A.f]) A=q.top(),q.pop();
47           used[A.f]=1;
48           if(A.f==T)
49           {
50                printf("%d",A.d);
51              break;
52           }
53           for(int i=first[A.f];i;i=next[i])
54               if(de[Rode[i].t]>A.d+Rode[i].d)
55               {
56                    de[Rode[i].t]=A.d+Rode[i].d;
57                    q.push((leaf){Rode[i].t,de[Rode[i].t]});
58               }
59      }
60      return 0;
61 } 

 

Bellman-Ford 算法

Bellman - ford算法是求含负权图的单源最短路径的一种算法,效率较低,代码难度较小。其原理为连续进行松弛,在每次松弛时把每条边都更新一下,若在n-1次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成。

 

点击前往百度百科的证明及思想

时间复杂度 O (nm)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<queue>
 5 using namespace std;
 6 
 7 int N,M,S,T,cnt,a,b,c;
 8 int first[2510],next[13000],de[2510];
 9 
10 struct maple{
11     int f,t,d;
12 }Rode[13000];
13 
14 void Build(int f,int t,int d)
15 {
16     Rode[++cnt]=(maple){f,t,d};
17     next[cnt]=first[f];
18     first[f]=cnt;
19 }
20 
21 bool Bellman_ford()
22 {
23      memset(de,63,sizeof(de));    
24      de[S]=0;
25      for(int i=1;i<N;++i)
26         for(int j=1;j<=cnt;++j)
27            if(de[Rode[j].t]>de[Rode[j].f]+Rode[j].d)
28                de[Rode[j].t]=de[Rode[j].f]+Rode[j].d;
29      for(int i=1;i<=cnt;++i) // 判负环 
30         if(de[Rode[i].t]>de[Rode[i].f]+Rode[i].d)
31             return false;
32      return true;  
33 }
34 int main()
35 {
36      scanf("%d%d%d%d",&N,&M,&S,&T);
37      for(int i=1;i<=M;++i)
38      {
39           scanf("%d%d%d",&a,&b,&c);
40           Build(a,b,c);
41           Build(b,a,c);
42      }
43      if(!Bellman_ford()) printf("-1");
44      else printf("%d",de[T]);
45      return 0;
46 } 

 

SPFA 算法 (Bellman-ford 算法的队列优化)

SPFA(Shortest Path Faster Algorithm)(队列优化)算法是求单源最短路径的一种算法,它还有一个重要的功能是判负环(在差分约束系统中会得以体现),在Bellman-ford算法的基础上加上一个队列优化,减少了冗余的松弛操作,是一种高效的最短路算法。

 

期望时间复杂度:O(me), 其中m为所有顶点进队的平均次数,可以证明m一般小于等于2n。

每次从队列里弹出一个点进行更新,同时把最短路径被更新的点压进队列里。

与bfs算法比较,复杂度相对稳定。但在稠密图中复杂度比迪杰斯特拉算法差。(摘自 baidu)

总复杂度(玄学)

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<queue>
 5 using namespace std;
 6 
 7 int N,M,S,T,cnt,a,b,c;
 8 int first[2510],next[13000],de[2510],rd[2510];
 9 bool used[2510];
10 
11 struct maple{
12     int f,t,d;
13 }Rode[13000];
14 
15 void Build(int f,int t,int d)
16 {
17     Rode[++cnt]=(maple){f,t,d};
18     next[cnt]=first[f];
19     first[f]=cnt;
20 }
21 
22 queue<int> q; 
23 bool SPFA()
24 {
25     memset(de,63,sizeof(de));
26     de[S]=0,rd[S]=1,used[S]=1;
27     q.push(S);
28     while(!q.empty())
29     {
30         int A=q.front();
31         q.pop();
32         used[A]=0;
33         for(int i=first[A];i;i=next[i])
34            if(de[Rode[i].t]>de[A]+Rode[i].d)
35            {
36                   de[Rode[i].t]=de[A]+Rode[i].d;
37                   if(!used[Rode[i].t])
38                   {
39                          used[Rode[i].t]=1;
40                          ++rd[Rode[i].t];
41                          q.push(Rode[i].t);
42                          if(rd[Rode[i].t]>N) // 判负环 
43                             return false;
44                   }
45            }
46     }
47     return true;
48 }
49 int main()
50 {
51      scanf("%d%d%d%d",&N,&M,&S,&T);
52      for(int i=1;i<=M;++i)
53      {
54           scanf("%d%d%d",&a,&b,&c);
55           Build(a,b,c);
56           Build(b,a,c);
57      }
58      if(!SPFA()) printf("-1");
59      else printf("%d",de[T]);
60      return 0;
61 } 

 

SPFA 的双端队列优化

入队时比较当前点和对头的最短路径长度,如果小于对头,就从头压入,否则就从尾压入。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<queue>
 5 using namespace std;
 6 
 7 int N,M,S,T,cnt,a,b,c;
 8 int first[2510],next[13000],de[2510],rd[2510];
 9 bool used[2510];
10 
11 struct maple{
12     int f,t,d;
13 }Rode[13000];
14 
15 void Build(int f,int t,int d)
16 {
17     Rode[++cnt]=(maple){f,t,d};
18     next[cnt]=first[f];
19     first[f]=cnt;
20 }
21 
22 deque<int> q; 
23 
24 bool SPFA()
25 {
26     memset(de,63,sizeof(de));
27     de[S]=0,rd[S]=1,used[S]=1;
28     q.push_back(S);
29     while(!q.empty())
30     {
31         int A=q.front();
32         q.pop_front();
33         used[A]=0;
34         for(int i=first[A];i;i=next[i])
35            if(de[Rode[i].t]>de[A]+Rode[i].d)
36            {
37                   de[Rode[i].t]=de[A]+Rode[i].d;
38                   if(!used[Rode[i].t])
39                   {
40                          used[Rode[i].t]=1;
41                    ++rd[Rode[i].t];
42                          if(!q.empty()&&de[Rode[i].t]<de[q.front()]) q.push_front(Rode[i].t);
43                          else q.push_back(Rode[i].t);
44                          if(rd[Rode[i].t]>N)  return false; // 判负环 
45                   }
46            }
47     }
48     return true;
49 }
50 int main()
51 {
52      scanf("%d%d%d%d",&N,&M,&S,&T);
53      for(int i=1;i<=M;++i)
54      {
55           scanf("%d%d%d",&a,&b,&c);
56           Build(a,b,c);
57           Build(b,a,c);
58      }
59      if(!SPFA()) printf("-1");
60      else printf("%d",de[T]);
61      return 0;
62 } 

 

分层图最短路

可以把de数组加一维来存状态解决分层图的问题。具体看题目。

 

最短路的例题 : codevs 1557 热浪 codevs 2273 扬帆远洋大战牧师妹酱

分层图最短路例题 :bzoj 2763 飞行路线 codevs 1391 伊吹萃香 

奇怪的最短路问题 :codevs 2218 补丁vs错误 codevs 1324 昂贵的聘礼 洛谷 1032 字串变换

 

 

 

 

 

 

 
 

 

转载于:https://www.cnblogs.com/maple-kingdom/p/maple-kingdom_this_is_a_happy_day.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值