最短路径—迪杰斯特拉算法入门

来求一波最短路径

首先先看看一道题(如果没学过的话就看看,学过了还看博客干嘛?)

http://ybt.ssoier.cn:8088/problem_show.php?pid=1381

对于没有学过的童鞋们来说,可能最先想到的是BFS,DP。

没错以上两个确实是基础的求最短路径的方法,但对于大部分最短路径的题来说,这是不实用的。

于是我们就要学习新的算法啦,(一脸无奈-_-!!!)。

首先来说迪杰斯特拉算法,(又是名字为五个字的高逼格算法)

算法思想:
设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。

其中我们把取了的电称为白点,还没取的点称为蓝点。

其实这其中也掺杂了动规的思想,

但注意!迪杰斯特拉算法无法处理负边权的情况(不要问我为什么)。

所以当题中说边权为负就别用了。

看看图人脑模拟一下!

先给出一个无向图

用Dijkstra算法找出以A为起点的单源最短路径步骤如下

来看看上面那道题的代码。

 1 #include<cstdio>
 2 #include<queue>
 3 #include<cstring>
 4 using namespace std;
 5 int f[2005][2005];
 6 bool f1[2005][2005];//记录边是否重复出现过。
 7 bool p[2005];//判断点有没有取过。
 8 int dis[2005];//记录每个点到起点的最短距离。
 9 const int maxx=999999999;
10 int main()
11 {
12     memset(f1,false,sizeof(f1));
13     memset(p,true,sizeof(p));
14     int n,m;
15     scanf("%d%d",&n,&m);
16     for(int i=1;i<=n;i++)
17     for(int j=1;j<=n;j++)
18     f[i][j]=maxx;//先把每条边的长度赋一个较大的值,
19                  //等同于先让所有点之间都不连通.
20     for(int i=1;i<=m;i++)
21     {
22         int a,b,w;
23         scanf("%d%d%d",&a,&b,&w);
24         if(f1[a][b]==false)
25         {
26             f[a][b]=w;
27             f[b][a]=w;
28             f1[a][b]=true;
29             f1[b][a]=true;
30         }
31         else
32         {
33             if(f[a][b]>w)
34             f[a][b]=w;
35         }
36         //注意检查同一条边的权值是否出现过多次
37         //如果出现过多次,按最小值计算。
38     }
39     for(int i=1;i<=n;i++)
40     dis[i]=f[1][i];
41     dis[1]=0;
42     p[1]=false;
43     for(int i=1;i<=n-1;i++)
44     {
45         int minn=maxx;
46         int k=0;
47         for(int j=1;j<=n;j++)
48         {
49             if(p[j]&&dis[j]<minn)
50             {
51                 minn=dis[j];
52                 k=j;
53             }
54         }
55         if(k==0)break;//如果没有找到点了,说明找完了。
56         p[k]=false;
57         for(int j=1;j<=n;j++)
58         {
59             if(dis[j]>dis[k]+f[k][j])
60             dis[j]=dis[k]+f[k][j];
61         }//更新每个点到起点的距离
62     }
63     if(dis[n]==maxx)
64     printf("-1");
65     else
66     printf("%d",dis[n]);
67     return 0;
68 }

总之迪杰斯特拉算法是一个贼实用的算法,相信在以后的比赛中会得以体现。

来到2012年普及组的最后一题练练!

https://www.luogu.org/problem/show?pid=1078

代码如下(做之前先别看!仅供参考!)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 int c[1005];
 6 int f[1005][1005];
 7 bool p[1005];
 8 int dis[1005];
 9 int mp[1005][1005];
10 int maxx=999999999;
11 int main()
12 {
13     memset(p,true,sizeof(p));
14     int n,k1,m,s,e;
15     scanf("%d%d%d%d%d",&n,&k1,&m,&s,&e);
16     for(int i=1;i<=n;i++)
17         scanf("%d",&c[i]);
18     for(int i=1;i<=k1;i++)
19         for(int j=1;j<=k1;j++)
20         scanf("%d",&f[i][j]);
21     for(int i=1;i<=n;i++)
22     for(int j=1;j<=n;j++)
23     mp[i][j]=maxx;
24     for(int i=1;i<=m;i++)
25     {
26         int a,b,w;
27         scanf("%d%d%d",&a,&b,&w);
28         if(f[c[a]][c[b]]==0)
29         mp[b][a]=w;
30         if(f[c[b]][c[a]]==0)
31         mp[a][b]=w;
32     }
33     for(int i=1;i<=n;i++)
34     dis[i]=mp[s][i];
35     dis[s]=0;
36     p[s]=false;
37     int minn,k;
38     for(int i=1;i<=n-1;i++)
39     {
40         minn=maxx;
41         k=0;
42         for(int j=1;j<=n;j++)
43         {
44             if(p[j]&&dis[j]<minn)
45             {
46                 k=j;
47                 minn=dis[j];
48             }
49         }
50         if(k==0)break;
51         p[k]=false;
52         for(int j=1;j<=n;j++)
53         {
54             if(dis[k]+mp[k][j]<dis[j])
55             dis[j]=dis[k]+mp[k][j];
56         }
57     }
58     if(dis[e]==maxx)
59     {
60         printf("-1");return 0;
61     }
62     printf("%d",dis[e]);
63     return 0;
64 }
原文地址:https://www.cnblogs.com/genius777/p/8470335.html