基础复习笔记-最短路

1.最短路是什么?

顾名思义,最短路就是求一个点到另一个点最短的路径,一半分为单源最短路和多源最短路。

2.最短路问题如何解决?

多源最短路问题:

多源最短路基本只有一条路可走(当询问次数很小时例外),那就是Flyod算法了。

Flyod

枚举k,i,j记住k一定要在最外层就好,每一次对于一个点进行松弛操作(就是更新dis),那么时间复杂度也是比较显然的O(n^3)

例题:luoguP2910寻宝之路

附上该题代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 int a[10005],f[105][105],ans;
 5 void floyd(int n)
 6 {
 7     for(int k=1;k<=n;k++)
 8     {
 9         for(int i=1;i<=n;i++)
10         {
11             for(int j=1;j<=n;j++)
12             {
13                 f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
14             }
15         }
16     }
17 }
18 int main()
19 {
20     int n,m;
21     scanf("%d%d",&n,&m);
22     for(int i=1;i<=m;i++)
23     {
24         scanf("%d",&a[i]);
25     }
26     for(int i=1;i<=n;i++)
27     {
28         for(int j=1;j<=n;j++)
29         {
30             scanf("%d",&f[i][j]);
31         }
32     }
33     floyd(n);
34     for(int i=2;i<=m;i++)
35     {
36         ans+=f[a[i-1]][a[i]];
37     }
38     printf("%d",ans);
39     return 0;
40 }

单源最短路问题:

1.Dijkstra算法:(不能处理负边权!!!)

Dijkstra算法的原理很简单,每一次取离原点最近的一个点,这样它已经是最短路,再用他来更新与他相邻的点的dis,不断更新下去即可。

那么终止条件就是不会有可以更新的点,时间复杂度是(N^2),找最近点可以用堆优化,那么就可以优化为((n+m)logn)

Dijkstra是一个相当稳定的算法,基本不会被卡死

例题:luoguP3371最短路(弱化版)  luoguP4779最短路(标准版)

附上两道题代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 struct EDGE
 5 {
 6     int to,nxt,v;
 7 }edge[500005];
 8 int head[10005],cnt,vis[10005],n,m,s;
 9 long long dis[10005];
10 void add(int a,int b,int c)
11 {
12     edge[++cnt].to=b;
13     edge[cnt].v=c;
14     edge[cnt].nxt=head[a];
15     head[a]=cnt;
16 }
17 void dijkstra()
18 {
19     for(int i=1; i<=n; i++)
20     {
21         dis[i]=2147483647;
22     }
23     dis[s]=0;
24     long long minn=2147483647;
25     int cur=s;
26     while(vis[cur]==0)
27     {
28         vis[cur]=1;
29         for(int i=head[cur]; i; i=edge[i].nxt)
30         {
31             dis[edge[i].to]=min(dis[cur]+edge[i].v,dis[edge[i].to]);
32         }
33         minn=2147483647;
34         for(int i=1;i<=n;i++)
35         {
36             if(vis[i]==0&&minn>dis[i])
37             {
38                 minn=dis[i];
39                 cur=i;
40             }
41         }
42     }
43 }
44 int main()
45 {
46     scanf("%d%d%d",&n,&m,&s);
47     for(int i=1; i<=m; i++)
48     {
49         int a,b,c;
50         scanf("%d%d%d",&a,&b,&c);
51         add(a,b,c);
52     }
53     dijkstra();
54     for(int i=1; i<=n; i++)
55     {
56         printf("%lld ",dis[i]);
57     }
58     return 0;
59 }
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int fr[100010],tl,n,m,x,y,z,s;;
 4 bool b[100010];
 5 long long d[100010];
 6 struct EDGE
 7 {
 8     int to,nxt,v;
 9 } edge[200005];
10 void add(int x,int y,int w)
11 {
12     edge[++tl].to=y;
13     edge[tl].v=w;
14     edge[tl].nxt=fr[x];
15     fr[x]=tl;
16 }
17 priority_queue< pair<int,int> > q;
18 void dijkstra()
19 {
20     for(int i=1; i<=n; i++)
21     {
22         d[i]=1e10;
23     }
24     d[s]=0;
25     q.push(make_pair(0,s));
26     while(q.empty()==0)
27     {
28         int x=q.top().second;
29         q.pop();
30         if(b[x]==1) continue;
31         b[x]=1;
32         for(int i=fr[x]; i; i=edge[i].nxt)
33         {
34             int y=edge[i].to,l=edge[i].v;
35             if(d[y]>d[x]+l)
36             {
37                 d[y]=d[x]+l;
38                 q.push(make_pair(-d[y],y));
39             }
40         }
41     }
42 }
43 int main()
44 {
45     scanf("%d%d%d",&n,&m,&s);
46     for(int i=1; i<=m; i++)
47     {
48         scanf("%d%d%d",&x,&y,&z);
49         add(x,y,z);
50     }
51     dijkstra();
52     for(int i=1; i<=n; i++) printf("%lld ",d[i]);
53     return 0;
54 }

2.SPFA(可以处理负边权!!!)

虽然说最近SPFA受到了针对,出题人利用SPFA的不稳定性轻松将其卡死,但是SPFA在解决负环和差分约束等问题还是有着很重大的意义

SPFA的原理更简单,就是开一个队列,每一次遇到dis可以更新的点就更新dis,如果它不再队列里就加入队列,当队列为空就得到了最短路。

而判断是否存在负环也很显然,一个点被更新了n-1次,那么就说明存在负环了。

例题:luoguP2299体委争夺战附上该题代码:

 1 #include<cstdio>
 2 #include<queue>
 3 #define maxm 200000
 4 #define maxn 2500
 5 #define inf 1e7
 6 using namespace std;
 7 
 8 int n,m,cnt;
 9 struct EDGE
10 {
11     int nxt,to,v;
12 };
13 EDGE edge[2*maxm+5];
14 int head[maxn+5],dis[maxn+5];
15 bool vis[maxn+5];
16 queue<int>q;
17 
18 void add(int x,int y,int z)
19 {
20     edge[++cnt].to=y;
21     edge[cnt].v=z;
22     edge[cnt].nxt=head[x];
23     head[x]=cnt;
24 }
25 void SPFA()
26 {
27     int s=1;
28     dis[s]=0;
29     for(int i=2;i<=n;i++)
30     {
31         dis[i]=inf;
32     }
33     q.push(s);
34     while(!q.empty())
35     {
36         s=q.front();
37         vis[s]=0;
38         q.pop();
39         for(int i=head[s];i;i=edge[i].nxt)
40         {
41             if(dis[edge[i].to]>dis[s]+edge[i].v)
42             {
43                 dis[edge[i].to]=dis[s]+edge[i].v;
44                 if(vis[edge[i].to]==0)
45                 {
46                     vis[edge[i].to]=1;
47                     q.push(edge[i].to);
48                 }
49             }
50         }
51     }
52 }
53 int main()
54 {
55     scanf("%d%d",&n,&m);
56     for(int i=1;i<=m;i++)
57     {
58         int x,y,z;
59         scanf("%d%d%d",&x,&y,&z);
60         add(x,y,z);
61         add(y,x,z);
62     }
63     SPFA();
64     printf("%d
",dis[n]);
65     return 0;
66 }

总而言之,对于不同的题目要选择最优的方法,并不存在哪个算法更重要,算法都一样好,就是看用的人了。。。

原文地址:https://www.cnblogs.com/yufenglin/p/10597420.html