双调旅行商问题

from: http://blog.csdn.net/fangxia722/archive/2008/10/10/3051450.aspx

一问题描述:    

      货郎问题(Traveling Salesman Problem,简称“TSP”)也叫货郎担问题,中国邮路问题,旅行商问题等,是计算机算法理论历史上的经典问题。在过去几十年中,它成为许多重要算法思想的测试平台,同时也促使一些新的理论领域的产生,比如多面体理论和复杂性理论。 货郎问题:给定n个结点和任意一对结点{i,j}之间的距离为dist(i,j),要求找出一条闭合的回路,该回路经过每个结点一次且仅一次,并且该回路的费用最小,这里的费用是指每段路径的距离和。 货郎问题求解其精确解是NP难的,并且求解任意常数因子近以度的解也是NP难的。若将问题限定在欧氏平面上,就成为欧氏平面上的货郎问题,也叫欧几里德旅行商问题(Eculid Traveling Salesman Problem)。但是,即使是欧氏平面上的货郎问题也是NP难的。因此通常用来解决TSP问题的解法都是近似算法。其中第一个欧几里德旅行商问题的多项式近似算法是Arora在1996年使用随机平面分割和动态规划方法给出的。

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

  

注:在一个单位栅格上显示的平面上的七个点。 a)最短闭合路线,长度大约是24.89。这个路线不是双调的。b)相同点的集合上的最短双调闭合路线。长度大约是25.58

二:问题分析

下面我以具体的题目为例谈谈双调欧几里德旅行商问题(bitonic tsp):

将一个人从最左端走到最右端,然后从最右端走到最左端等价成两个人同时从最左端不重复的走过中间的点并且到达最右端。

我们不妨设这两个人为A和B,且总是假定走在前面的人是A。再设函数F(x1, x2)表示A走到x1的位置,B走到x2的位置,并且所有x1,x2之前的位置都被不重复的走过的最短距离之和。根据前面的假设我们可以知道 x1>=x2,且F(xn,xn)即为所得。

下面的过程就是如何推导出状态方程,我把它划分为三种情况:

1) 如果x1=x2,即A和B处在同一点,那么F(x1, x2) = F(x1, x1) = F(x1, x1 - 1) + dist(x1, x1 - 1)

2) 如果x1=x2+1,即B在A的紧邻的靠后一点,那么F(x1, x2) = F(x2, x') + dist(x1, x') (1<= x' <=x2)

3) 如果x1>x2+1,即B离A在后面一个距离的范围以上,那么F(x1, x2) = F(x1 - 1, x2) + dist(x1, x1 - 1)

其实这里不用考虑如果x1,x2走到中间同一个点,因为很容易得到那样不是最优的。

以上算法时间复杂度与空间复杂度均为O(n*n)

三 代码

package dynamic_progranmming;

/**
 * @author wangzhendong
 *
 */
public class BitonicTour {
    /*
     * 私有变量
     */
 private double[][] B;//定义从i到j的最短距离(其中j始终在i的前边)
 private double[][] W;//定义每两个点之间的距离
 private double[] X;//定义X坐标
 private double[] Y;//定义Y坐标
 private int n;//定义点的个数
 /*
  * 构造函数
  */
 public BitonicTour(double[] X,double[] Y,int n) {
  // TODO Auto-generated constructor stub
  this.X=X;
  this.Y=Y;
  this.n=n;
  W=new double[n][n];
  B=new double[n][n];
  for(int i=0;i<n;i++)
  {
   for(int j=0;j<n;j++)
   {
    W[i][j]=Math.sqrt((X[i]-X[j])*(X[i]-X[j])+(Y[i]-Y[j])*(Y[i]-Y[j]));
    B[i][j]=1000000;
   }
  }
 }
   /*
    *实际计算最优路线
    */
 public void bitonictour()
 {
  B[0][0]=0;
  for(int i=1;i<n;i++)
  {
   for(int j=0;j<i;j++)
   {
    if(i==j+1)
    {
       for(int k=0;k<=j;k++)
       {
        double temp=B[j][k]+W[k][i];
        if(temp<B[i][j])
        {
         B[i][j]=temp;
        }
       }
    }
    else if(j<i-1)
    {
     B[i][j]=B[i-1][j]+W[i][i-1];
    }
    /*
    else if(j==i)
    {
     for(int u=0;u<j;u++)
        {
         double temp=B[j][u]+W[u][i]+W[i-1][i];
         if(temp<B[i][j])
         {
          B[i][j]=temp;
         }
        }
    }*/
    else
    {
     continue;
    }
   }
  }
  System.out.println(B[n-1][n-2]);
 
 }
 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
       double[] X={0,1,2,5,6,7,8};
       double[] Y={6,0,3,4,1,5,2};
       BitonicTour bt=new BitonicTour(X,Y,7);
       bt.bitonictour();
 }

}

 


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/fangxia722/archive/2008/10/10/3051450.aspx

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值