zoukankan      html  css  js  c++  java
  • 欧几里得旅行商问题 java与c++实现

    双调欧几里得旅行商问题是一个经典动态规划问题。《算法导论(第二版)》思考题15-1

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

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

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


    以上截取来自:http://blog.csdn.net/xiajun07061225/article/details/8092247



    /**
     * 欧几里得旅行商(travelling salesman problem)问题
     *   分解模型:
     *        一个人从Pi严格递减的走到P1,然后再严格递增的回到Pj,求总路径的最小值,其中P1,P2,...,Pj的
     *        所有节点都要走一次,求最短的路径;
     *        对于1 <= i <= j <= n, 我们定义P(i, j)是一条包含了P1, P2, P3 ...Pj的途径
     *        这条路径可以分成2部分:递减序列与递增序列:起点是Pi(1 <= i <= j),拐点是P1,
     *        终点是Pj, P[i, j]为其最小值;路径为(Pi-_>Pm-->P1-->Pn-->Pj,其中1<m<i<n<j) 
     *   动态规划方程:
     *   	  p[i,j]为最小值,dist(m,n)为m与n的直接距离
     *        p[1,2]=dist(1,2)
     *        i<j-1时,从P(i+1)到Pj之间的点必然出现在递增序列中
     *              p[i,j]=p[i,j-1]+dist(j-1,j)
     *        i=j-1时,假设P1到Pi之间的一个中间节点Pk,使得p[k,i]+dist(k,j)最小,也就是说,找到了
     *              将P1到Pj的所有节点走一遍的最短路径
     *              p[i,j]=min{p[k,i]+dist(k,j)}(其中1<=k<=i)
     *        p[k,i]又可以按照以上方式继续找出最小路径
     *        
     *        对于欧几里得旅行商问题,要求的最短路径是p[n,n]
     *        p[n,n]=p[n-1,n]+dist(n-1,n);
     *       
     *   
     */
    </pre><pre name="code" class="html">

    以下是C++实现,来自:http://blog.csdn.net/xiajun07061225/article/details/8092247

    #include <iostream>
    #include <cmath>
    #include <iomanip>
    
    using namespace std;
    
    const int n = 7;//点的数目
    const int MaxVal = 999999;
    const int MaxLen = 201;
    
    struct tagPoint{
    	double x,y;
    };
    
    //计算点i和点j之间的直线距离
    double distance(tagPoint *points,int i,int j)
    {
    	return sqrt((points[i].x - points[j].x) * (points[i].x - points[j].x) +
    		(points[i].y - points[j].y) * (points[i].y - points[j].y));
    }
    
    double DP(tagPoint *points,int n)
    {
    	double b[MaxLen][MaxLen];//记录最短路径的长度
    	//计算所有情况下的b[i][j],1 <= i <= j
    	//初始化
    	b[1][2] = distance(points,1,2);
    	for (int j = 3;j <= n;++j)
    	{
    		//i < j-1
    		for (int i = 1;i <= j - 2;++i)
    		{
    			b[i][j] = b[i][j - 1] + distance(points,j - 1,j);
    		}
    		//i = j - 1,b[i][j] = min(b[k][j - 1] + distance(k,j));
    		b[j - 1][j] = MaxVal;
    		for (int k = 1;k <= j - 2;++k)
    		{
    			double temp = b[k][j - 1] + distance(points,k,j);
    			if (temp < b[j - 1][j])
    			{
    				b[j - 1][j] = temp;
    			}
    		}
    	}
    
    	b[n][n] = b[n - 1][n] + distance(points,n - 1,n);
    
    	return b[n][n];
    }
    
    int main()
    {
    	int NUM;
    	while(cin >> NUM)
    	{
    		tagPoint *points = new tagPoint[NUM + 1];
    		for (int i = 1;i <= NUM;++i)
    		{
    			cin >> points[i].x;
    			cin >> points[i].y;
    		}
    		double minDis = DP(points,NUM);
    		//设置输出格式:精确到小数点后2位
    		cout.setf(ios::fixed);
    		cout << setprecision(2) << minDis << endl;
    	}
    }


    以下是java实现,基本思想是一样的。

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.Scanner;
    
    import org.omg.CORBA.PRIVATE_MEMBER;
    
    /**
     * @date 2014-09-20
     * @author acer
     * 欧几里得旅行商(travelling salesman problem)问题
     *   分解模型:
     *        一个人从Pi严格递减的走到P1,然后再严格递增的回到Pj,求总路径的最小值,其中P1,P2,...,Pj的
     *        所有节点都要走一次,求最短的路径;
     *        对于1 <= i <= j <= n, 我们定义P(i, j)是一条包含了P1, P2, P3 ...Pj的途径
     *        这条路径可以分成2部分:递减序列与递增序列:起点是Pi(1 <= i <= j),拐点是P1,
     *        终点是Pj, P[i, j]为其最小值;路径为(Pi-_>Pm-->P1-->Pn-->Pj,其中1<m<i<n<j) 
     *   动态规划方程:
     *   	  p[i,j]为最小值,dist(m,n)为m与n的直接距离
     *        p[1,2]=dist(1,2)
     *        i<j-1时,从P(i+1)到Pj之间的点必然出现在递增序列中
     *              p[i,j]=p[i,j-1]+dist(j-1,j)
     *        i=j-1时,假设P1到Pi之间的一个中间节点Pk,使得p[k,i]+dist(k,j)最小,也就是说,找到了
     *              将P1到Pj的所有节点走一遍的最短路径
     *              p[i,j]=min{p[k,i]+dist(k,j)}(其中1<=k<=i)
     *        p[k,i]又可以按照以上方式继续找出最小路径
     *        
     *        对于欧几里得旅行商问题,要求的最短路径是p[n,n]
     *        p[n,n]=p[n-1,n]+dist(n-1,n);
     *        解释:Pn-->Pn,如果第一个点是Pn-1,那么上面的式子显然成立;
     *        	      如果第一个点是Pk,(1<=k<=n-2),那么Pn-1一定在Pk-->Pn的递增序列中,所以
     *            p[n,n]=p[k,n]+dist(k,n)=p[k,n-1]+dist(n-1,n)+dist(k,n);
     *            其中p[n-1,n]=p[k,n-1]+dist(k,n)
     *            所以 p[n,n]=p[n-1,n]+dist(n-1,n)必然成立;
     *       
     *   
     */
    public class TSProblem {
    
    	
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		String  line=null;//读取每一行的输入数据
    		int[][] pointXY;//记录横纵坐标
    		
    		System.out.print("请输入点的个数N:");
    		InputStreamReader isr=new InputStreamReader(System.in);
    		BufferedReader bReader=new BufferedReader(isr);
    		try {
    			line=bReader.readLine().toString();
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		int n=Integer.parseInt(line);//记录点的个数
    		pointXY=new int[n][2];
    		
    		System.out.println("请输入每个点的坐标,坐标格式为:x,y  (以EOF结束输入)");
    		Scanner scn=new Scanner(System.in);
    		int i=0;
    		while(!"EOF".equals(line=scn.nextLine())){
    			String[] p=line.split(",");
    			int j=0;
    			for(String str:p){
    				pointXY[i][j++]=Integer.parseInt(str);			
    			}
    			i++;
    			if(i>=n){
    				System.out.println("输入的点数已经足够,自动结束输入");
    				break;//退出输入
    			}
    			
    		}
    		
    		//System.out.println("ok");
    		int length=pointXY.length;
    		TSProblem tsp=new TSProblem(pointXY,length);
    		
    		
    		
    
    	}
    	
    	public TSProblem(int[][] points,int length){
    		//points中存储所有的节点,乱序排列
    		//1、首先将所有的节点按照x坐标顺序排列,可选用多种排序方式,这里采用快排,时间复杂度为o(nlogn)	
    		points=this.QuickSort(points, 0, length-1);
    //		//打印排序后的点
    //		for(int i=0;i<length;i++){
    //			System.out.println(points[i][0]+","+points[i][1]);
    //		}
    		
    		//2、找到最短路径
    		double leastDis=DP(points, length);
    		
    		System.out.println("最短路径为:"+leastDis);
    		
    		
    		
    	}
    	
    	
    	//计算最短旅行路径
    	double DP(int[][] points,int length){
    		
    		//p用于记录最短路径的长度
    		double[][] p=new double[length][length];
    		p[0][1]=dist(points, 0, 1);
    		for(int j=2;j<length;j++){
    			//case1:i<j-1
    			// p[i,j]=p[i,j-1]+dist(j-1,j)
    			for(int i=0;i<j-1;i++){
    				p[i][j]=p[i][j-1]+dist(points, i, j);
    			}
    			
    			//case2:i=j-1
    			// p[i,j]=min{p[k,i]+dist(k,j)}(其中1<=k<=i)
    			p[j-1][j]=Float.MAX_VALUE;
    			double temp=0;
    			for(int k=0;k<j-1;k++){
    				temp=p[k][j-1]+dist(points, k, j);
    				if(temp<p[j-1][j]){
    					p[j-1][j]=temp;
    				}
    			}
    			
    			
    		}
    		p[length-1][length-1]=p[length-2][length-1]+dist(points, length-2, length-1);
    		
    		//返回Pn-->Pn,即最短路径
    		return p[length-1][length-1];
    		
    	}
    	
    	//计算两点之间的直接距离dist(i,j)
    	double dist(int[][] point ,int i,int j){
    		double distance=0;
    		distance=Math.sqrt(Math.pow((point[j][0] -point[i][0]),2)
    				           +Math.pow((point[j][1]-point[i][1]),2));
    		
    		return distance;		
    	}
    	
    	//快排
    	int[][] QuickSort(int[][] num,int left,int right){
    		/**
    		 * 1、先从数列中取出一个数作为基数
    		 * 2、大数置于该数的右边,小数置于该数的左边
    		 * 3、重复步骤1,2
    		 */
    		if(left<right){
    			int tempX=num[left][0];//设置基数,不唯一
    			int tempY=num[left][1];
    			int i=left,j=right;
    			while(i<j){
    				while(i<j&&num[j][0]>=tempX)
    					j--;	//右边的数小于基数时退出
    				if(i<j){
    					//如果在右边找到一个数小于基数,num[i]此时为空,将该数填到num[i]
    					num[i][0]=num[j][0];
    					num[i][1]=num[j][1];
    					i++;
    				}
    				while(i<j&&num[i][0]<tempX)
    					i++;	//左边的数大于基数时退出
    				if(i<j){
    					//如果在左边找到一个数大于基数,num[j]此时为空,将该数填到num[j]
    					num[j][0]=num[i][0];
    					num[j][1]=num[j][1];
    					j--;
    				}
    							
    			}//当i=j时退出
    			//num[i]为空值,将基数填入
    			num[i][0]=tempX;
    			num[i][1]=tempY;
    			
    			QuickSort(num, left, i-1);//左边快排
    			QuickSort(num, i+1, right);//右边快排		
    			
    		}
    		//返回已经排好序的数组
    		return num;	
    	}
    	
    }
    
    
    








    
       
    
    
  • 相关阅读:
    PHP基础学习笔记(一)
    安装wampserver之后,浏览器中输入localhost页面显示IIS7解决办法
    HTML5常识总结(一)
    AngularJs中的服务
    AngularJs中的directives(指令part1)
    Happy Number——LeetCode
    Binary Tree Zigzag Level Order Traversal——LeetCode
    Construct Binary Tree from Preorder and Inorder Traversal——LeetCode
    Construct Binary Tree from Inorder and Postorder Traversal——LeetCode
    Convert Sorted Array to Binary Search Tree——LeetCode
  • 原文地址:https://www.cnblogs.com/wennian/p/5036925.html
Copyright © 2011-2022 走看看