【算法学习】双调欧几里得旅行商问题(动态规划)

604 篇文章 8 订阅
579 篇文章 5 订阅

双调欧几里得旅行商问题是一个经典动态规划问题。《算法导论(第二版)》思考题15-1和北京大学OJ2677都出现了这个题目。

旅行商问题描述:平面上n个点,确定一条连接各点的最短闭合旅程。这个解的一般形式为NP的(在多项式时间内可以求出)

J.L. Bentley 建议通过只考虑双调旅程(bitonictour)来简化问题,这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点。下图(b)显示了同样的7个点的最短双调路线。在这种情况下,多项式的算法是可能的。事实上,存在确定的最优双调路线的O(n*n)时间的算法。

上图中,a是最短闭合路线,这个路线不是双调的。b是最短双调闭合路线。


------------------------------------------------我是分割线------------------------------

思路:1),首先将所有点加上坐标,x轴指向右,y轴指向下。然后将所有点按照x轴坐标从小到大排列。

2)总体思路是依次从排好序的节点取出一个节点,决定该节点应该放在第一条路径上还是第二条路径上。  3)定义一个数组:double b[8][8]; //b[i][j]表示第一条路径搜索到第i各节点,第二条路径搜索到第j个节点后的最短路径长度。如果i==j则说明两条路径汇聚到i点上

如果i==n则说明 搜索到终点

3)求两点距离的方法:double Length(Node x[],int i,int j)

4)m[i][j]存的是编号 i 点与 编号 j 点的最短距离。需要声明的是:b[i][j]=b[j][i];

所以,m矩阵是一个对称矩阵。即 i 点与 j 点的距离跟j点与i点的矩阵相等。所以这里我们只需要求下三角矩阵b就可以。

5)由以上思路得递归公式:(i>j 求的是下三角)

  (i==j时): b[i][j]=b[i][j-1]+Length[i][j-1]   
         (i>j+1时):b[i][j]= b[i-1][j]+Length[i-1][i]  
         (i=j+1时):b[i][j]=min(1<=k<j)(b[k][j]+Length[k][i]) //j==1时b[i][j]=Length(i,j);

由以上分析可以得到如下代码:

[cpp] view plaincopy

    //双调欧几里得旅行商问题  
    #include<iostream>  
    #include<math.h>  
    #define M 65536  
    using namespace std;  
    //定义节点坐标  
    struct Node  
    {  
        int x;  
        int y;  
    }N[10];  
    //求节点i和节点j之间的长度  
    double Length(Node *N,int i,int j)  
    {  
        double L;  
        L=sqrt(double((N[i].x-N[j].x)*(N[i].x-N[j].x)+(N[i].y-N[j].y)*(N[i].y-N[j].y)));  
        return L;  
    }  
    void ShortPath(Node*N ,double (*b)[10],int length)  
    {  
        int i,j,k;  
        double num;  
        //定义起始节点序号为1  
        b[1][1]=0;  
        for(i=2;i<=length;i++)  
        {  
            for(j=1;j<=i;j++)  
            {  
                //如果两条路径终点都是i,则总路径的长度为一条从1到i与  
                //一条从1到i-1的路径之和加上从i-1到i的距离。  
                if(i==j)  
                {  
                    b[i][j]=b[i][j-1]+Length(N,i,j-1);  
                }  
                //如果i与j之间相隔一个点以上,则j点的路径不变,而i点的路径是  
                //从1到i-1的路径加上i-1到i的路径  
                if(i>j+1)  
                {  
                    b[i][j]=b[i-1][j]+Length(N,i-1,i);  
                }  
                if(i==j+1)  
                {  
                    b[i][j]=M;  
                    if(j==1)  
                    {  
                        b[i][j]=Length(N,i,j);  
                    }  
                    for(k=1;k<j;k++)  
                    {  
                        num=b[k][j]+Length(N,k,i);  
                        if(b[i][j]>num)  
                        {  
                            b[i][j]=num;  
                        }  
                    }  
                }  
                b[j][i]=b[i][j];  
            }  
        }  
    }  
    int main()  
    {  
        N[1].x=0;  
        N[1].y=0;  
        N[2].x=1;  
        N[2].y=6;  
        N[3].x=2;  
        N[3].y=3;  
        N[4].x=5;  
        N[4].y=2;  
        N[5].x=6;  
        N[5].y=5;  
        N[6].x=7;  
        N[6].y=1;  
        N[7].x=8;  
        N[7].y=4;  
        double b[10][10]={0};  
        ShortPath(N,b,7);  
        cout<<b[7][7]<<endl;  
        return 0;  
    }  

ps:为什么在j=i-1的时候,可以实现多个路径,比如:{6,5}--6--4--3--1--2--5--6实现环路,代码中只检查d[k,j]+d[k,i],这明明只有单个点的过度??-----留待思考

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您解答算法导论第15章的双调欧旅行问题和整齐打印问题。 1. 双调欧旅行问题 双调欧旅行问题是指在平面上给定一些点,求一条简单闭合曲线,使得经过每个点恰好一次,且总路程最小。其中,欧几得距离是指两点之间的距离。 该问题可以通过分治算法进行求解,具体步骤如下: 1)将所有的点按照x坐标从小到大排序; 2)将所有的点分成两部分,分别求出每一部分的最小路径,分别记为d1和d2; 3)在两部分的点中,选择一个点p,使得p在上一部分的最后一个点,同时p在下一部分的第一个点; 4)以p为分界点,将所有点分成上下两部分,并分别按照y坐标从小到大排序; 5)分别计算上半部分和下半部分的最小路径,分别记为d3和d4; 6)最终结果为d1+d2+d3+d4。 2. 整齐打印 整齐打印问题是指将一段文本分成若干行,每行不超过给定的宽度,使得每一行的长度尽可能相等,同时在每行末尾添加空格,使得每行的末尾恰好是一个单词的末尾,且每行的空格数最小。 该问题可以通过动态规划算法进行求解,具体步骤如下: 1)定义一个cost数组,其中cost[i][j]表示将第i个单词到第j个单词放在一行的代价; 2)定义一个lc数组,其中lc[i][j]表示将第i个单词到第j个单词放在一行的空格数; 3)计算cost和lc数组,具体方法如下: - 对于任意的i<=j,将第i到第j个单词放在一行,计算该行的空格数; - 如果该行的长度超过给定的宽度,则该方案不可行,否则将该方案的代价和空格数存入cost和lc数组中。 4)定义一个dp数组,其中dp[i]表示将前i个单词分成若干行的最小代价; 5)动态规划求解dp数组,具体方法如下: - 对于任意的1<=i<=n,将前i个单词分成若干行,计算最小代价; - 设最后一行的单词范围为[j+1, i],则dp[i] = min(dp[j] + cost[j+1][i]),其中j的范围为0<=j<i。 6)最终结果为dp[n]。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值