**无向题要注意对称性
求最短路径的基本思想
按照最短路径的长度递增次序,依次求得——源点到其余各点的最短路径!
路径长度最短的最短路径的特点:
在这条路径上,必定只含有一条弧,并且这条弧的权值最小。
最短最短路径是直达的;
//第二近的城市要么是直达,要么是经过最短最短的路径转一下。
松弛操作:
{六百公里的而直达绳子,经由转放在五百公里的路径上,绳子松弛了。}
路径长度次短的最短路径的特点:
它只可能有两种情况(第二近):
或者是直接从原点到该点(只含一条弧);
或者是从原点进故宫顶点v1,在到达该顶点(有两条弧组成)。
路径长度第三短的路径的特点:
它可能有三种情况:
1、直接从原点到该点(只含一条弧);
2、从源点经过顶点v1,在到达该顶点(有两条弧组成);
3、从原点经过顶点v2,在到达该顶点。
其中1、2在上一步已经做过比较,所以——每次只需要比较2个值!
其余最短路径的特点:
或者是:直接从源点到该点(只含一条弧);
或者是:从源点金国已求得最短路径的顶点,再到达该顶点。
迪杰斯特拉算法
0)准备工作:
设置辅助数组Dist,其中每个分量Dist[k]表示:
当前所求得的从源点你到其余各顶点K的最短路径。
Dist表示从起点到其余各点的路径长度,如果没有直达则可以设为最大。
(只记录目前最短的,每求出一个最短路径,都要根据这一个点更新,每求出一个就要转一下)
1)在所有从源点出发的弧中选取一条权值最小的弧,即为第一条最短路径。
Dist[k]=Map[v0][k] V0和k之间存在弧
Dist[k]=Map[INF] V0和k之间不存在弧
其中的最小值即为最短的最短路径的长度。
**
状态转移方程?
理论表示:Dist[k]=min(Map[I][k]+Dist[i])//已经求出的新的最短路径+这个给点到k点的路径,如果松弛了,就更新一下。下面这个是实际编程,更好理解
其中,i为已经求出最短路的节点的节点编号。
假设u为刚刚求得的最短路节点,则:
实际编程:Dist[k]=min(Dist[k],Map[u][k]+Dist[u]);
举个例子,求第五短的路径时,只要比较**第从起点到第四短到第五段的距离与历史求过多次最优解比较一下,就可以。因为从第二短到第五短的距离已经松弛过了,其余比第五短的也都与第五个松弛过了。
解释:因为两点之间的最短路径,也包含其它两点之间的最短路径!(动态规划的最优子结构)
思考:
求两点之间的最短路径和求一个点到其余所有点的最短路径,时间复杂度一样。
因为求出的最短路径可能是最坏的最短路径,有可能要挨个求一下,最坏情况就是求到。
//举个例子:
#include<bits/stdc++.h>
#include<stdio.h>
#define inf 0x7FFFFFFF
#define M 201
using namespace std;
int Map[M][M],Dist[M],visit[M];
/*struct AB{
int a;
int b;
int x;
}str[1000];
*/
int main (){
int m,n,i,j,a,b,dis,start,Min,next,targe;
while(scanf("%d%d",&n,&m)==2){
for(i=0;i<n;i++){
visit[i]=0;
Dist[i]=inf;
for(j=0;j<n;j++){
Map[i][j]=inf;
}
}
while(m--){
scanf("%d%d%d",&a,&b,&dis);
Map[a][b]=min(Map[a][b],dis);//可能有多条路,只记录最短路
Map[b][a]=Map[a][b];//无向图;
}
scanf("%d%d",&start,&targe);
Dist[start]=0;
visit[start]=1;//是否已经求出最短路径;
while(start!=targe){
Min=inf;
//
for(i=0;i<n;i++){
if(Map[start][i]!=inf)//如果可达就松弛操作
Dist[i]=min(Dist[i],Map[start][i]+Dist[start]);
if(visit[i]&&Dist[i]<Min){
next=i;//最近城市
Min=Dist[i];
}
}
//
if(Min==inf) break;
start = next ;
visit[start] = 0;
}
if(Dist[targe]==inf) puts("-1");
else printf("%d\n",Dist[targe]) ;
}
return 0;
}