最短路径算法
Dijkstra算法
Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。注意该算法要求图中不存在负权边。
算法思想
设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。
例题及解题思路
poj 2387
题目大意:有N个点,给出从a点到b点的距离,当然a和b是互相可以抵达的,问从1到n的最短距离
#include<stdio.h>
#include<iostream>
using namespace std;
#define max 0x3f3f3f3f;
int mp[max][max];
int dis[i];
int vis[i];
int m,n;
void init(int n){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
mp[i][j]=max;
}
}
void dij(int n){
for(int i=1;i<=n;i++)dis[i]=mp[1][i];
vis[1]=1;
int k,minn;
for(int i=1;i<=n;i++){
minn=max;
for(int j=1;j<=n;j++){
if(!vis[j]&&dis[j]<minn){
minn=dis[j];
k=j;
}
}
vis[k]=1;
for(int j=1;j<=n;j++){
if(dis[j]>dis[k]+mp[k][j])
dis[j]=dis[k]+mp[k][j];
}
}
}
int main(){
scanf("%d%d",&m,&n);
int x,y,z;
for(i=1; i<=m; i++)
{
scanf("%d%d%d",&x,&y,&z);
mp[x][y]=z;
mp[y][x]=z;
}
init(n);
dij(n);
cout<<dis[n]<<endl;
return 0;
}
Floyd算法
解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包。Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2)。
算法思想
Floyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释(这个诠释正是动态规划最富创造力的精华所在)
从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
核心代码:
for(k=0;k<n;k++){
for(i=0;i<n;i++)
for(j=0;j<n;j++){
if(A[i][j]>(A[i][j]+A[k][j])){
A[i][j]=A[i][k]+A[k][j];
path[i][j]=k;
}
}
}