1、我们以E所有边的集合,而边的权重则由权重函数w:E?0,?定义。因此,w(u,v)就是从顶点u到顶点v的非负花费值(cost)。边的花费可以想像成两个顶点之间的距离。任两点间路径的花费值,就是该路径上所有边的花费值总和。已知有V中有顶点s及t,Dijkstra算法可以找到s到t的最低花费路径.?最短路径)。这个算法也可以在一个图中,找到从一个顶点s到任何其他顶点的最短路径。具体算法见附录。2.动态规划法这里先讨论用动态规划法的解法。考虑多段图的最短路径问题的填表形式。用一个数组costn作为存储子问题解的表格,costi表示从顶点i到终点n-1的最短路径,数组pathn存储状态,pathi表示
2、从顶点i到终点n-1的路径上顶点i的下一个顶点。则: costi=mincij+costj (ijn且顶点j是顶点i的邻接点) (式)pathi=j (使cij+costj最小的j) (式)对多段图的边(u, v),用cuv表示边上的权值,将从源点s到终点t的最短路径记为d(s, t),则从源点0到终点9的最短路径d(0, 9)由下式确定:d(0, 9)=minc01+d(1, 9), c02+d(2, 9), c03+d(3, 9),这是最后一个阶段的决策,它依赖于d(1, 9)、d(2, 9)和d(3, 9)的计算结果,而由此模式推知,d(1, 9)=minc14+d(4, 9), c15
3、+d(5, 9),d(2, 9)=minc24+d(4, 9), c25+d(5, 9), c26+d(6, 9),d(3, 9)=minc35+d(5, 9), c36+d(6, 9),每一个d(i,n-1)都是通过mincik+d(k,n-1)得到的ki&kn-1;再往后推的过程和以上的过程类似,将这些产生式得到以后,会发现他们的求解除了两点之间的代价外,在例子中,他们都依赖于d(7, 9)=c79和d(8, 9)=c89,而他们都是可以从图上直接得到的。这样再从末尾一层一层往上推就可以得到最终的答案了。算法主要由三部分组成:第一部分是初始化部分,其时间性能为O(n);第二部分是依次计算各
4、个顶点到终点的最短路径,由两层嵌套的循环组成,外层循环执行n-1次,内层循环对所有出边进行计算,并且在所有循环中,每条出边只计算一次。假定图的边数为m,则这部分的时间性能是O(m);第三部分是输出最短路径经过的顶点,其时间性能是O(n)。所以,算法的时间复杂性为O(n+m)。为了实现时间的分析,在程序后添加了输出运行时间的函数,以便于对比分析。具体算法、具体代码及实验结果见附录1。3.分支限界法再讨论当用分支限界法用来解决多段图路径问题的过程:首先对该多段图应用贪心法求得近似解,并算出其代价路径。将其作为多段图最短路径问题的上界。而把每一段最小的代价相加,可以得到一个非常简单的下界。于是,就可
5、以得到了目标函数的一个大致的范围。由于多段图将顶点划分为k个互不相交的子集,所以,多段图划分为k段,一旦某条路径的一些段被确定后,就可以并入这些信息并计算部分解的目标函数值的下界。一般情况下,对于一个正在生成的路径,假设已经确定了i段(1ik),其路径为(r1, r2, , ri, ri+1),此时,该部分解的目标函数值的计算方法即限界函数如下:应用分支限界法同样求解附录中图所示多段图的最短路径问题,具体的搜索过程是这样进行的,首先考虑根节点,根据限界函数算出目标函数的值,然后下一个结点的选择在本例中有三种情况, 这里每种情况下的目标函数值下界都要算出来并且加以比较,下界的计算方法为除了加上选
6、定点与初始点之间的距离外,以后的第一个点选择一选定点为初始点到下段最小代价的路径,以后的段与段之间的代价都按他们之间最小的代价来计算。这样再加上根节点与初始阶段之间的最小代价,就得到这种情况下的解了。在得到的代价中,找出最小的代价,并以之为初始结点循环往下做,直到到达目标结点。结论: 程序的运行截图如附录所示。分析各个方法的整个过程得到以下思考:1.贪心法、动态规划法和分支限界法都可以用来解决多段最短路径问题,然而在这种情况相比之下,贪心法的运算效率比较高,因为它不像另外两种方法一样,需要涉及到许多的点。由于这里并没有找到函数有效地给程序计时(time函数很不精确,而对于小程序来说,就没有什么
7、参考性)。因此这里我们就以本题的数据为例,用一个笨方法,看各个方法访问了多少数据,可以看到,动态规划法由于需要填表,并有一个相关的迭代问题,它几乎涉及了所有的点;而分支限界法,它通过贪心法设置的上下限,并以他们为依据进行剪枝,减少了许多的运算量。而贪心法,访问了最少的点。2.就结果准确性来看,就本题例子来看,贪心法结果为0 2 4 7 9,路径的代价为20;动态分配法采取的路径为:0 3 5 8 9,路径的代价为16;而分支限界法,结果为0 3 5 8 9,路径的代价为16。可以看出,在这个方面,动态分配法和分支限界法都达到了预期的结果,相比直线,贪心法的误差就比较大了。由以上的讨论,我们可以
8、看出分支限界法的综合性能比较好,他和动态规划法在解决多段最短路径问题时都可以得到正确解,而贪心法虽然可以省时间与空间,但结果不准确是它的缺点。各方法都是有利有弊的。因此在选择方法时,还应当根据实际情况。当只需要大概的一个解时,当然是要用省时省力的贪心法;如果对结果又比较高的要求的话,那么就要采取动态规划法或分支限界法。那么dijkstra算法呢,他的明显优点就是它的多用性,他可以求任意一点到其他某一点的距离,但是他访问的数据量很大,几乎要访问所有的边(相对于贪心法而言),因此这里来说,在单纯的解决多段最短路径问题时,他们的功能都差不多,而在解决其他较复杂的图时,Dijkstra算法有明显的优越
9、性,但当然,作为贪心法的一种,他的结果的准确性不是那么的高。Dijkstra算法在本质上为贪心算法,每一步的选择为当前步的最优,复杂度为O(n*n)。动态规划法是可以看作是对分支限界法的改进。分支限界算法,每一步的扩散为当前耗散度的最优,复杂度为(没算)其实,他们各有各的优缺点,可以尝试将他们混合起来用,扬长避短,像动态规划法和分支限界法,我们是不是可以试着在动态规划法的过程中像分支限界法里一样,设置范围,并且过程中对肯定不会是最后结果的数据“剪枝”。这样就可以提高运行速率了。结论(必须精确、有条理、清晰与简要):建议(直接从结论中得出):附录Dijstra算法(边的拓展)While(!(每一
10、个dv=最短路径)If(存在一条从u到v的边) If(du+w(u,v)=0; i-) 对顶点i的每一个邻接点j,根据式计算costi; 根据式计算pathi;3输出最短路径长度cost0;4. 输出最短路径经过的顶点: i=0 循环直到pathi=n-1 输出pathi; i=pathi;用分支限界法求解多段图的最短路径问题的算法:1根据限界函数计算目标函数的下界down;采用贪心法得到上界up; 2将待处理结点表PT初始化为空; 3for (i=1; i=1) 对顶点u的所有邻接点v 根据式计算目标函数值lb; 若lb=up,则将i,lb存储在表PT中; 如果i= =k-1且叶子结点的lb
11、值在表PT中最小, 则输出该叶子结点对应的最优解; 否则,如果i= =k-1且表PT中的叶子结点的lb值不是最小,则 up=表PT中的叶子结点最小的lb值; 将表PT中目标函数值超出up的结点删除; u=表PT中lb最小的结点的v值; i=表PT中lb最小的结点的i值;i+;动态规划法解决多段图最短路径问题:/*多段图最短路径问题总结:costi表示从顶点i到终点n-1的最短路径,pathi表示从顶点i到终点n-1的路径上顶点i的下一个顶点;下面的公式重点:costi=minc(ij)+costjpathi=使c(ij)+costj最小的j; c(ij)表示i和j顶点之间的距离*/具体代码如下
12、:#includectime#define INFINITY 32767#define MAX 20_int64 start,end;int min6,Part;typedef struct char vexsMAX; /顶点信息 int vexnum,arcnum; int arcsMAXMAX; /保存两个顶点之间的边长Graph; /图的结构体struct node int part,node1,node2,lb,previous; struct node *next;void CreateGraph(Graph &G)/初始化多段图 int i,j; start=clock(); pr
13、intf(请输入顶点数和边数:); /scanf(%d %d,&; =10; /顶点数 =18; /边的数 for(i=0;ii+) i=i; for(j=0;jj+) ij=INFINITY;请按以下格式输入边的代价(顶点1 顶点2 两点之间边的代价,顶点标号从0开始):n for(k=0;k+) scanf(%d %d %d,i,j,ij);int getDown(Graph G)/分支限界法求下界 int j,k,i,n,n0,down=0,initial620;/initial数组用来存储第i段有哪些结点 min0=INFINITY;6;20; initialij=0; j=0; if
14、0iINFINITY) initial0j+=i; if0imin0) min0=0i; Part=1; down+=mini; i=0; while(initiali+0!= n=0;j=0;mini=INFINITY; while(initiali-1j+!=0) k=0;n0=n; while(k+) ifinitiali-1j-1k-1 initialin+=k-1; ifinitiali-1j-1k-1mini) mini=initiali-1j-1k-1; if(mini down+=mini; Part+; return down;int Greedy(Graph G,int f
15、lag,int up)/贪心法 int min=INFINITY,m=flag;%d,flag); for(int i=0; ifmimin) min=mi; flag=i; if(flag up=Greedy(G,flag,up); else printf(%dn return up+min;void path(Graph G,int up)/分支限界法 int down,i,j,k,u,lb,flag=1,previous=0; struct node *p,*end,*PT,*q,*ST,*End,*mins; down=getDown(G);n若用分支限界法,该题的结果取值范围为%d,
16、%d。,down,up); PT=NULL;end=NULL;ST=NULL,End=NULL;/PT用来存储合格的点,ST表用来存储由于拓展被删除的点 i=1;u=0; /求解第i段,u表示顶点,u是那个已确定的顶点 while(flag) / 对顶点u的所有邻接点v / 根据式计算目标函数值lb; / 若lbuj) for(k=0;k+)/确定了结点以后找到由他出发最小的代价 ifjkmini) mini=jk; lb=uj+mini; for(int l=i+1;l0)/计算lb lb+=PreviousU; while(Previous! p=PT; while(p!=NULL) if
17、(p-node1=Previous&p-node2=U) lb+=PreviousU; U=Previous; Previous=p-previous; break; if(lbpart=i+1;node1=u;node2=j;lb=lb;next=NULL;previous=previous; printf(%d-%d,代价%d,上一个结点为%dn,p-node1,p-node2,p-lb,p-previous); if(PT=NULL) PT=p; else end-next=p; end=p; end- q=PT;lb=INFINITY; while(q!=NULL) if(q-lblb
18、; q=q-next; printf(%d %d lb=%d,mins-node1,mins-node2,lb); if(p=q & end-node2node2; arrayPart-2=p-node1; i=Part-3; while(i arrayi=p-i-; q=PT; while(q-node2!=arrayp-previous) q+; if(i0) arrayi-1=q- p=q; printf(最短路径为: for(i=0; printf(,arrayi); if(inode1=mins-node1&node2=mins-node2)/删除PT链表中已被拓展的结点并把他添加到ST链表中 if(ST=NULL) ST=p- else End-next=p- End=p- End- PT=PT- /printf(删除的结点是:%d %dnnext-node2); /*else while(p-next!node2) if(ST=NULL) ST=p- else End- End=p- p-
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1