带权图
为了研究最短路径,首先引入带权边,带权图的概念:
设图G=<V,E>(无向图或有向图),给定W:E->R,对于G的每一条边e,称W(e)为边e的权,这样的图称为带权图,记为G=<V,E,W>.
当e=(u,v)或e=<u,v>时,把W(e)记作W(u,v).
设P是G中的一条通路,P中所有边的权之和称作P的长度,记作W(P). 即W(P)=∑\sum∑W(e) (e∈\in∈E(p)).
类似地可以定义回路C的长度W(C)
最短路径
设带权图G=<V,E,W>,其中每一条边e的权W(e)为非负实数,对于V中任意两个顶点u,v,当uv连通时,称从u到v长度最短的路径为从u到v的最短路径,称其长度为从u到v的最短距离,记作d(u,v),约定d(u,u)=0,当u,v不连通时,d(u,v)=∞\infty∞
最短路径问题即是给定带权图G=<V,E,W>以及顶点u,v,其中每一条边的权都为非负实数,求u到v的最短路径。
我们可以看出:若u v1 v2 v3…vk是u到v的最短路径,则对每一个t(1<=t<=k),u v1 v2 v3…vt是u到vt的最短路径。Dijkstra算法即是根据该条性质总结出的算法。
Dijkstra算法
附上教材给的过程,个人感觉还是有点难懂的:
算法给出了从给定某点s到每一个点的最短路径。在计算过程中,赋予每一个顶点v一个标号l(v)=(l1(v),l2(v)),标号分为永久标号与临时标号。在v的永久标号l(v)中,l2(v)是从s到v的距离,l1(v)是s到v的最短路径上v的前一个顶点,当l(v)是临时标号时,l1(v)和l2(v)分别是当前从s经过永久标号的顶点到v的最短路径上v的前一个顶点和这条路径的长度。
过程:
输入:带权图G<V,E,W>和s∈\in∈V,其中|V|=n
输出:s到G中每一点的最短路径和距离
1.令ls<-(s,0),l(v)<-(s,+∞\infty∞) (v∈\in∈V-{s}),I<-1,l(s)是永久标号,其余标号均为临时标号,u<-s
2.for与u关联的临时标号的顶点v
3. if l2(u)+W(u,v)<l2(v) then 令l(v)<-(u,l2(v)+W(u,v))
4. 计算l2(t)=min{l2(v)|v∈\in∈V且有临时标号},并把l(s)改为永久标号
5. if i<n then 令u<-t,i<-i+1,重复第二步
分析:
需要分配的空间:
两个矩阵(二维数组)用于存放图和单源路径
两个一维数组存最短距离和访问标记
其他的一些数据
主要的思路个人感觉和贪心有点像,先找到源点可达的最短的路径,然后更新距离的数组,以到达的点为起点继续找最短路径,这样路径长度就能很简单地得到了。然后是路径输出,这个还想了一会儿,我的想法是用一个路径数组,每次判断距离数组要更新时,将记下的之前的最短路覆盖该路,然后再在末尾添加上新到达的结点。第一次初始化更新时需要同时初始化好路径数组的第一列和第二列(可达的项)。
上代码:
#include<iostream>
#include<vector>
#include<string.h>
using std::vector;
const int MAXN = 101;
const int INF = 0x3f3f3f3f;
//description:this is a template of Algorithm-Dijkstra
int min(int A,int B)
{
if(A<B)return A;
else return B;
}//Get the min value
//
int m,n;
int visited[MAXN],Distance[MAXN];
int Matrix[MAXN][MAXN],Path[MAXN][MAXN];
//vector<int> Path[MAXN];
//define 1.VisitSign 2.temp Distance(min) 3.The Graph 4.path
//
void CreateGraph()
{
memset(Path,0,sizeof(Path));
memset(Matrix,INF,sizeof(Matrix));
int Estart,Eend,Weight;
std::cin>>n>>m;//input vertexnum and edgenum
for(int i=1;i<=m;i++)
{
std::cin>>Estart>>Eend>>Weight;
Matrix[Estart][Eend]=Weight;
Matrix[Eend][Estart]=Weight;
//assume that it is a normal graph
}
for(int i=1;i<=n;i++)
{
Matrix[i][i]=0;
}
}//Get the Graph
//
void PrintGraph()
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
std::cout<<Matrix[i][j]<<" ";
}
std::cout<<"\n";
}
}//print the graph to test
//
void Dijkstra(int st)
{
memset(Distance,INF,sizeof(Distance));
visited[st]=1;
for(int i=1;i<=n;i++)
{
Path[i][1]=st;
Distance[i]=min(Distance[i],Matrix[st][i]);
if(Distance[i]==Matrix[st][i]&&i!=1)
{
Path[i][2]=i;//Initialize
}
}
for(int i=1;i<=n-1;i++)
{
int CurrentMinDis=INF;
for(int j=1;j<=n;j++)
{
if(visited[j]==0&&CurrentMinDis>Distance[j])
{
CurrentMinDis=Distance[j];
st=j;
}
}//find the minimum length path avaliable
visited[st]=1;//sign "visited"
for(int j=1;j<=n;j++)
{
if(Matrix[st][j]+Distance[st]<Distance[j])
{
for(int k=1;k<MAXN;k++)
{
Path[j][k]=Path[st][k];
if(Path[st][k]==0)
{
Path[j][k]=j;
break;
//update the path array
}
}
}
Distance[j]=min(Distance[j],Matrix[st][j]+Distance[st]);
//update the distance array
}
}
}//Dijkstra Algorithm
//
int main()
{
/* vector<int>::iterator iter1;*/
CreateGraph();
PrintGraph();
Dijkstra(1);
for(int i=1;i<=n;i++)
{
std::cout<<Distance[i]<<" ";
}
std::cout<<std::endl;
std::cout<<std::endl;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(Path[i][j]==0)
{
std::cout<<"end"<<std::endl;
break;
}
else
{
std::cout<<Path[i][j]<<"-";
}
}
}
return 0;
}
弄了半个下午,好像路径输出上还是有点小问题,有时间再改改
测试:
5 6
1 2 5
1 3 8
2 3 1
2 4 3
4 5 7
2 5 2
7 11
1 2 3
2 5 6
5 7 2
7 6 2
6 4 2
4 1 5
1 3 7
3 4 3
3 5 3
2 3 2
4 7 8
一组是教材上的例题,一组是在网上找的>_<