zoukankan      html  css  js  c++  java
  • 洛谷【P1523】旅行商的背包(算法导论 15-1) 题解

    P1523 旅行商简化版

    题目背景

    欧几里德旅行商((Euclidean Traveling Salesman))问题也就是货郎担问题一直是困扰全世界数学家、计算机学家的著名问题。现有的算法都没有办法在确定型机器上在多项式时间内求出最优解,但是有办法在多项式时间内求出一个较优解。

    为了简化问题,而且保证能在多项式时间内求出最优解,(J.L.Bentley)提出了一种叫做(bitonic) (tour)的哈密尔顿环游。它的要求是任意两点((a,b))之间的相互到达的代价(dist(a,b)=dist(b,a))且任意两点之间可以相互到达,并且环游的路线只能是从最西端单向到最东端,再单项返回最西端,并且是一个哈密尔顿回路。

    题目描述

    著名的(NPC)难题的简化版本

    现在笛卡尔平面上有(n(n<=1000))个点,每个点的坐标为((x,y))((-2^{31}<x,y<2^{31}),且为整数),任意两点之间相互到达的代价为这两点的欧几里德距离,现要你编程求出最短(bitonic) (tour)

    输入输出格式

    输入格式:

    第一行一个整数(n)

    接下来(n)行,每行两个整数(x,y),表示某个点的坐标。

    输入中保证没有重复的两点,

    保证最西端和最东端都只有一个点。

    输出格式:

    一行,即最短回路的长度,保留(2)位小数。

    输入输出样例

    输入样例#1:

    7
    0 6
    1 0
    2 3
    5 4
    6 1
    7 5
    8 2
    

    输出样例#1:

    25.58
    

    说明

    来源 (Source)

    《算法导论(第二版)》 (15-1)


    评成绿题稍微有一点低,这个题目的思维要求还是比较高的。

    这里利用到一个重要的思想:对于一个有确切起点和终点的回路,其路径情况,可以认为是同一个位置出发的两个点同时向前推进,类似的题目可以参考NOIP2008—传纸条

    所以不要被这个题目的题面欺骗。实际上题目问的,就是从一个起点出发的,具有相同终点的两条路径的最小总长度!

    做法可以认为是一类套路,如下:

    (F[i][j])为从最左侧的起点出发,一个点走到(i),一个点走到(j(i>j)),且所有(i)以内的点全部被走过的最短路径。

    • (i=j+1) 时:(F[i][j])可以由(F[j][k],k∈[1,j))转移得到,原因如下图
    • (i>j+1)时,走在前面的人上一步一定是(i-1),所以本情况一定由(F[i-1][j])转移而来。

    总复杂度(O(n^2)),思维难度还是有的。

    (Code:)

    //P1523 100pts 
    #include<cmath> 
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define INF 1e50 
    #define MAXN 1010
    #define dbl double
    using namespace std;
    struct node{
    	dbl x;
    	dbl y;
    	bool operator<(const node &rhs)const{
    		return x<rhs.x;
    	}//按照纵坐标从左往右排序 
    }arr[MAXN];
    int n;
    dbl f[MAXN][MAXN];
    inline dbl dis(int x,int y){
    	return hypot(fabs(arr[x].x-arr[y].x),fabs(arr[x].y-arr[y].y));
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i){
    		scanf("%lf%lf",&arr[i].x,&arr[i].y);
    	} 
    	sort(arr+1,arr+1+n);//排序
    //	for(int i=1;i<=n;++i){
    //		printf("%lf %lf
    ",arr[i].x,arr[i].y);
    //	}
    	for(int i=0;i<=1000;++i){
    		for(int j=0;j<=1000;++j){
    			f[i][j]=INF;
    		}
    	}
    	f[2][1]=dis(1,2);
    //	printf("f[2][1]=%lf
    ",f[2][1]);
    	for(int i=3;i<=n;++i){
    		for(int j=i-1;j>=1;--j){
    			if(i==j+1){
    				//刚好是向后一个的节点
    				for(int k=j-1;k>=1;--k){
    					f[i][j]=min(f[i][j],f[j][k]+dis(k,i));
    				} 
    			}else{
    				f[i][j]=min(f[i][j],f[i-1][j]+dis(i-1,i));
    			}
    		}
    	}
    	double ans=INF;
    	for(int i=1;i<=n;++i){
    		ans=min(ans,f[n][i]+dis(i,n));
    	}
    //		printf("
    ");
    	
    	printf("%.2lf
    ",ans);
    }
    
  • 相关阅读:
    vim
    Linux 软链接和硬链接
    常用命令
    linux 二级目录结构
    关于bash shell的理解
    虚拟机网络模式
    安装虚拟机
    date 命令
    使用3种协议搭建yum仓库
    ubuntu update时发生错误
  • 原文地址:https://www.cnblogs.com/maomao9173/p/9871455.html
Copyright © 2011-2022 走看看