最短路.docx
- 文档编号:30392273
- 上传时间:2023-08-14
- 格式:DOCX
- 页数:27
- 大小:132.93KB
最短路.docx
《最短路.docx》由会员分享,可在线阅读,更多相关《最短路.docx(27页珍藏版)》请在冰豆网上搜索。
最短路
单单源最短路径问题,即在图中求出给定顶点到其它任一顶点的最短路径。
在弄清楚如何求算单源最短路径问题之前,必须弄清楚最短路径的最优子结构性质。
一.最短路径的最优子结构性质
该性质描述为:
如果P(i,j)={Vi....Vk..Vs...Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径。
下面证明该性质的正确性。
假设P(i,j)={Vi....Vk..Vs...Vj}是从顶点i到j的最短路径,则有P(i,j)=P(i,k)+P(k,s)+P(s,j)。
而P(k,s)不是从k到s的最短距离,那么必定存在另一条从k到s的最短路径P'(k,s),那么P'(i,j)=P(i,k)+P'(k,s)+P(s,j)
则与P(i,j)是从i到j的最短路径相矛盾。
因此该性质得证。
二.Dijkstra算法
由上述性质可知,如果存在一条从i到j的最短路径(Vi.....Vk,Vj),Vk是Vj前面的一顶点。
那么(Vi...Vk)也必定是从i到k的最短路径。
为了求出最短路径,Dijkstra就提出了以最短路径长度递增,逐次生成最短路径的算法。
譬如对于源顶点V0,首先选择其直接相邻的顶点中长度最短的顶点Vi,那么当前已知可得从V0到达Vj顶点的最短距离dist[j]=min{dist[j],dist[i]+matrix[i][j]}。
根据这种思路,
假设存在G=
1.从V-U中选择使dist[i]值最小的顶点i,将i加入到U中;
2.更新与i直接相邻顶点的dist值。
(dist[j]=min{dist[j],dist[i]+matrix[i][j]})
3.知道U=V,停止。
代码实现:
/*Dijkstra求单源最短路径2010.8.26*/
#include
#include
#defineM100
#defineN100
usingnamespacestd;
typedefstructnode
{
intmatrix[N][M];//邻接矩阵
intn;//顶点数
inte;//边数
}MGraph;
voidDijkstraPath(MGraphg,int*dist,int*path,intv0)//v0表示源顶点
{
inti,j,k;
bool*visited=(bool*)malloc(sizeof(bool)*g.n);
for(i=0;i { if(g.matrix[v0][i]>0&&i! =v0) { dist[i]=g.matrix[v0][i]; path[i]=v0;//path记录最短路径上从v0到i的前一个顶点 } else { dist[i]=INT_MAX;//若i不与v0直接相邻,则权值置为无穷大 path[i]=-1; } visited[i]=false; path[v0]=v0; dist[v0]=0; } visited[v0]=true; for(i=1;i { intmin=INT_MAX; intu; for(j=0;j { if(visited[j]==false&&dist[j] { min=dist[j]; u=j; } } visited[u]=true; for(k=0;k { if(visited[k]==false&&g.matrix[u][k]>0&&min+g.matrix[u][k] { dist[k]=min+g.matrix[u][k]; path[k]=u; } } } } voidshowPath(int*path,intv,intv0)//打印最短路径上的各个顶点 { stack intu=v; while(v! =v0) { s.push(v); v=path[v]; } s.push(v); while(! s.empty()) { cout< s.pop(); } } intmain(intargc,char*argv[]) { intn,e;//表示输入的顶点数和边数 while(cin>>n>>e&&e! =0) { inti,j; ints,t,w;//表示存在一条边s->t,权值为w MGraphg; intv0; int*dist=(int*)malloc(sizeof(int)*n); int*path=(int*)malloc(sizeof(int)*n); for(i=0;i for(j=0;j g.matrix[i][j]=0; g.n=n; g.e=e; for(i=0;i { cin>>s>>t>>w; g.matrix[s][t]=w; } cin>>v0;//输入源顶点 DijkstraPath(g,dist,path,v0); for(i=0;i { if(i! =v0) { showPath(path,i,v0); cout< } } } return0; } 测试数据: 运行结果: 贪心算法Dijkstra单源最短路径 2011-05-0520: 35: 12| 分类: 算法 | 标签: dijkstra 最短路径 |字号 订阅 单源最短路径问题定义: 给定一个图G=(V,E),其中每条边的权是一个非负实数。 另外给定V中的一个顶点v,称为源。 求从源v到所有其它各个顶点的最短路径。 单源最短路径贪心选择策略: 选择当前从源v出发用最短的路径所到达的顶点,这就是目前的局部最优解。 Dijkstra算法思想: 首先设置一个集合S;用数组dis[]来记录v到S中各点的当前最短路径长度。 然后不断地用贪心选择来扩充这个集合,并同时记录或修订数组dis[];直至S包含所有V中顶点。 具体算法的例子: Description 用Dijkstra算法实现单源最短路径问题。 Input 第一行: n。 代表n个顶点。 其中第一个顶点为源点第二行: c11c12c13....c1n(以下n行合起来为n*n的权矩阵,cij代表了i点到j点的边的权值,-1代表无穷大.每行n个数,数与数之间空格隔开)第三行: c21c22c23....c2n...第n行: cn1cn2n Output 1-2: 最短距离(第i点到1的最短距离,冒号后面无空格,直接是最短距离的值。 )1-3: 最短距离...1-n: 最短距离 SampleInput 5010-13010010050-1-1-1500201030-120060100-110600 SampleOutput 1-2: 101-3: 501-4: 301-5: 60 代码: #include #include intconstmaxint=10000000; voidDijkstra(intn,intv,int*dist,int*prev,int**c) { //建立保存已找到最近的节点 bool*s; s=(bool*)malloc(sizeof(bool)*(n+1)); //循环初始化原点到各边的距离 for(inti=1;i<=n;i++) { dist[i]=c[v][i]; s[i]=false; if(dist[i]==-1) prev[i]=0; else prev[i]=v; } dist[v]=0; s[v]=true; for(into=1;o<=n;o++) { //设置最大值为 inttemp=maxint; //默认为原点 intu=v; //循环,不断从dist中找出距离原点最近的节点,记录到s[]中。 for(intj=1;j<=n;j++) { if(! s[j]&&(dist[j]! =-1)&&(dist[j] { u=j; temp=dist[j]; } } //将其加入已找到的最短距离的数组中 s[u]=true; //修改dist[]里面的值,根据贪心性质,记录到余下节点的最短的值 for(intk=1;k<=n;k++) { if(! s[k]&&(c[u][k]! =-1)) { intnewdist=dist[u]+c[u][k]; //修改原点到个边的距离 if((newdist { dist[k]=newdist; prev[k]=u; } } } } } intmain(void) { //顶点的个数 intn; scanf("%d",&n); //分配数组空间 int**c; c=(int**)malloc(sizeof(int)*(n+1)); //分配记录最短距离数组 int*dist; dist=(int*)malloc(sizeof(int)*(n+1)); dist[0]=-1; //分配记录前驱数组 int*prev; prev=(int*)malloc(sizeof(int)*(n+1)); prev[0]=-1; //矩阵,从下标1开始 for(inti=1;i<=n;i++) { c[i]=(int*)malloc(sizeof(int)*(n+1)); c[i][0]=-1; } //读取矩阵数据 for(intj=1;j<=n;j++) { for(intk=1;k<=n;k++) { scanf("%d",&c[j][k]); } } //执行Dijkstra算法 Dijkstra(n,1,dist,prev,c); //回归输出结果 for(intk=2;k<=n;k++) { printf("1-%d: %d\n",k,dist[k]); } return0; } 第一种解法: Floyd算法 算法实现: 使用一个邻接矩阵存储边权值,两两之间能访问的必为一个有限的数,不能访问则为无穷大(用2^29代替)。 注意自身和自身距离为0。 对于一对顶点u和v,看看是否存在一个断点w使得从u经过w到v比已知的路径更短(包含原始输入中从u直接到v的路程)。 对所有顶点进行如上松弛操作,得到的结果是两点之间的最短路程,也可判断两点是否连通。 算法缺点: 普通的Floyd算法时间复杂度为O(n^3),对于数据较多的情况容易TLE。 但解决本题HDU1874完全足够。 Floyd算法耗时62MS C语言: 高亮代码由发芽网提供 #include #defineMax0xfffffff int n,m,map[201][201]; int min(int a,int b) { return a>b? b: a; } void getmap() { int i,j,a,b,l; for(i=0;i for(j=0;j map[i][j]=(i==j? 0: Max); for(i=0;i { scanf("%d%d%d",&a,&b,&l); map[a][b]=map[b][a]=min(map[a][b],l); } } void floyd(int s,int e) { int i,j,k; for(k=0;k for(i=0;i for(j=0;j map[i][j]=min(map[i][j],map[i][k]+map[k][j]); printf("%dn",map[s][e] map[s][e]: -1); } int main() { int s,e; while(~scanf("%d%d",&n,&m)) { getmap(); scanf("%d%d",&s,&e); floyd(s,e); } return 0; } 第二种解法: Dijkstra算法 这个算法比较经典,一般的最短路径都可以用这个来解决,耗时也比较少,不过不能处理负权路径 按路径长度递增次序产生最短路径算法: 把V分成两组: (1)S: 已求出最短路径的顶点的集合 (2)V-S=T: 尚未确定最短路径的顶点集合 将T中顶点按最短路径递增的次序加入到S中, 保证: (1)从源点V0到S中各顶点的最短路径长度都不大于 从V0到T中任何顶点的最短路径长度 (2)每个顶点对应一个距离值 S中顶点: 从V0到此顶点的最短路径长度 T中顶点: 从V0到此顶点的只包括S中顶点作中间 顶点的最短路径长度 依据: 可以证明V0到T中顶点Vk的最短路径,或是从V0到Vk的 直接路径的权值;或是从V0经S中顶点到Vk的路径权值之和 (反证法可证) 求最短路径步骤 算法步骤如下: 1.初使时令S={V0},T={其余顶点},T中顶点对应的距离值 若存在 若不存在 2.从T中选取一个其距离值为最小的顶点W且不在S中,加入S 3.对T中顶点的距离值进行修改: 若加进W作中间顶点,从V0到Vi的 距离值比不加W的路径要短,则修改此距离值 重复上述步骤2、3,直到S中包含所有顶点,即S=T为止 Dijkstra算法耗时0MS C语言: 高亮代码由发芽网提供 #include #include #definemax999999 int map[201][201]; int n,m,st,en,f[201],mark[201]; void Dijkstra() { int i,j,k,min; memset(mark,0,sizeof(mark)); for(i=0;i f[i]=map[st][i]; f[st]=0; for(i=0;i { min=max; for(j=0;j { if(! mark[j]&&f[j] { min=f[j]; k=j; } } if(min==max)break; mark[k]=1; for(j=0;j if(! mark[j]&&f[j]>f[k]+map[k][j]) f[j]=f[k]+map[k][j]; } if(f[en]! =max)printf("%dn",f[en]); else printf("-1n"); } int main() { int x,y,z,i,j; while(scanf("%d%d",&n,&m)! =EOF) { for(i=0;i<=n-1;i++) for(j=0;j<=n-1;j++) map[i][j]=max; for(i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&z); if(map[x][y]>z) {map[x][y]=map[y][x]=z;} } scanf("%d%d",&st,&en); Dijkstra(); } return 0; } 第三种解法: Bellman_Ford算法 这个算法也比较好理解,就是不断松弛操作,处理含有负权的路径 对有向图G(V,E),用贝尔曼-福特算法求以Vs为源点的最短路径的过程: 建立dist[]、Pred[],且dist[s]=0,其余赋 。 Pred[]表示某节点路径上的父节点 对 ,比较dist[Vi]+(Vi,Vj)和dist[Vj],并将小的赋给dist[Vj],如果修改了dist[V_j]则pred[Vj]= Vi(松弛操作) 重复以上操作V −1次 再重复操作一次,如dist[Vj]> dist[Vi]+(Vi,Vj),则此图存在负权环。 伪代码: C语言: 高亮代码由发芽网提供 For i: =1 to |V|-1 do //v为顶点数 For 每条边(u,v)∈E do //对每条边进行遍历 Relax(u,v,w); For每条边(u,v)∈E do //判断是否存在负权环 If dis[u]+w Bellman_Ford算法耗时15MS, 代码: C语言: 高亮代码由发芽网提供 #include #defineMAX1047483647 int m,n,d[208],start,tar; struct project{ int x,y,w; }map[2008]; void Bellman_Ford() { int i,j; for(i=0;i
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 短路