来求一波最短路径
首先先看看一道题(如果没学过的话就看看,学过了还看博客干嘛?)
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中的顶点为中间顶点的当前最短路径长度。
其中我们把取了的电称为白点,还没取的点称为蓝点。
其实这其中也掺杂了动规的思想,
但注意!迪杰斯特拉算法无法处理负边权的情况(不要问我为什么)。
所以当题中说边权为负就别用了。
看看图人脑模拟一下!
来看看上面那道题的代码。
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 }