1.问题描述:给出一个有向图G,图中的每一条边都有一个非负边权,要求找出从图的源顶点s到目标顶点t之间的最短路径。
例图:从左到右从上到下,序号从0开始依次增大,即顶点个数n=11,A=s,E=t
2.问题分析:
(1)分支限界法:
算法从G的源点s和空队列开始。结点s被扩展之后,他的儿子结点2,3,4被一次插入队列当中。然后取出队头元素,进行下一步扩展。保证每一次扩展时,源到当前节点的和都是最小的。具体的解空间图如下:
扫描二维码关注公众号,回复: 4554265 查看本文章
算法过程
算法先从源节点s开始扩展,3个子结点2,3,4被插入到队列当中,如下图所示。
取出结点2,它有3个子树。结点2沿边f扩展到3时,路径长度为5,而结点3的当前路径为3(s->6),没有得到优化,该子树被剪掉。.结点2沿边d,e扩展值5,6时,将他们加入优先队列,如图
取出头结点3,它有两个子树。结点3沿f边扩展到结点6时,该路径长度为12,而结点6的当前路径为4,该路径没有被优化,该子树被剪枝。结点3沿g扩展到7时,将7加入到优先队列。如下如所示
重复上面操作直到队列为空。s到各个结点的最短路径。
(2)Dijkstra算法:
将图G中所有的顶点V分成两个顶点集合S和T。以v为源点已经确定了最短路径的终点并入S集合中,S初始时只含顶点v,T则是尚未确定到源点v最短路径的顶点集合。然后每次从T集合中选择S集合点中到T路径最短的那个点,并加入到集合S中,并把这个点从集合T删除。直到T集合为空为止。
具体步骤
1、选一顶点v为源点,并视从源点v出发的所有边为到各顶点的最短路径(确定数据结构:因为求的是最短路径,所以①就要用一个记录从源点v到其它各顶点的路径长度数组dist[],开始时,dist是源点v到顶点i的直接边长度,即dist中记录的是邻接阵的第v行。②设一个用来记录从源点到其它顶点的路径数组path[],path中存放路径上第i个顶点的前驱顶点)。
2、在上述的最短路径dist[]中选一条最短的,并将其终点(即<v,k>)k加入到集合s中。
3、调整T中各顶点到源点v的最短路径。 因为当顶点k加入到集合s中后,源点v到T中剩余的其它顶点j就又增加了经过顶点k到达j的路径,这条路径可能要比源点v到j原来的最短的还要短。调整方法是比较dist[k]+g[k,j]与dist[j],取其中的较小者。
4、再选出一个到源点v路径长度最小的顶点k,从T中删去后加入S中,再回去到第三步,如此重复,直到集合S中的包含图G的所有顶点。
#include <iostream>
using namespace std;
#define NoEdge -1 //表示无边
#define MAXVALUE 10000
int n=11;//顶点个数
int dist[11];//记录从某源顶点v到各顶点的最短距离
int s[11];//已经确定了最短路径的终点
int t[11];//尚未确定到源点v最短路径的顶点集合
int prev[11];//记录每个顶点最短路径上的前驱顶点,根据该数组可以找到任何一个顶点到达源顶点v的最短路径
int e[11][11];//记录顶点之间边的权值(-1表示无边)
int FindIndexOfMin(int dist[])//找到最短的路径
{
int index=0;
int minc=MAXVALUE;
for(int i=0; i<n; i++)
{
if(minc>dist[i]&&dist[i]!=NoEdge&&s[i]==0)
{
minc=dist[i];
index=i;
}
}
return index;
}
bool isEmpty(int t[])//判断未确定最短路径的顶点集合t是否为空
{
for(int i=0; i<n; i++)
if(t[i]==1)return false;
return true;
}
void Init()
{
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
e[i][j]=-1;
e[0][1]=5;
e[0][2]=3;
e[1][3]=1;
e[1][4]=3;
e[1][5]=6;
e[2][4]=8;
e[2][5]=7;
e[2][6]=6;
e[3][7]=6;
e[3][8]=8;
e[4][7]=3;
e[4][8]=5;
e[5][8]=3;
e[5][9]=3;
e[6][8]=8;
e[6][9]=4;
e[7][10]=3;
e[8][10]=2;
e[9][10]=2;
}
void ShortestPath2(int v)//分支限界法
{
//活结点表x[n],各顶点到源顶点最短距离dis[n],最短路径的前驱顶点pre[n]
int x[n],dis[n],pre[n];
for(int i=0; i<n; i++) //将与源顶点v相连的顶点加入活结点表
{
if(e[v][i]!=NoEdge)
{
x[i]=1;
pre[i]=v;
}
else
{
x[i]=0;
pre[i]=i;
}
dis[i]=e[v][i];
}
while(true)
{
int index=NoEdge;
for(int i=0; i<n; i++)//从当前活结点表取出一个新的扩展结点
{
if(x[i]==1)
{
index=i;
x[i]=0;
break;
}
}
if(index==NoEdge)break;//活结点表空则退出循环
for(int i=0; i<n; i++)
{//判断经过该扩展结点到达它的子结点的最短路径是否比原记录的最短路径小
if(e[index][i]!=NoEdge&&(dis[index]+e[index][i]<dis[i]||dis[i]==NoEdge))
{
x[i]=1;
dis[i]=dis[index]+e[index][i];
pre[i]=index;
}
}
}
for(int i=0; i<n; i++)
cout<<dis[i]<<" ";
cout<<endl;
for(int i=0; i<n; i++)
cout<<pre[i]<<" ";
cout<<endl;
}
void ShortestPath(int v)//Dijkstra算法
{
for(int i=0; i<n; i++)
{
prev[i]=i;
dist[i]=e[v][i];
t[i]=1;
s[i]=0;
}
int m=n;
while(--m>0)//(isEmpty(t)==false)
{
int index=FindIndexOfMin(dist);
s[index]=1;
t[index]=0;
if(prev[index]==index)prev[index]=v;
for(int i=0; i<n; i++)
{
if(t[i]==1&&e[index][i]!=NoEdge&&dist[index]!=NoEdge&&(e[index][i]+dist[index]<dist[i]||dist[i]==NoEdge))
{
dist[i]=e[index][i]+dist[index];
prev[i]=index;
}
}
}
}
int main()
{
Init();
ShortestPath2(0);
/*for(int i=0; i<n; i++)
cout<<prev[i]<<" ";
cout<<endl;
for(int i=0; i<n; i++)
cout<<dist[i]<<" ";
cout<<endl;*/
}