旅行商简化版

背景

欧几里德旅行商(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

样例输入1

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

样例输出1

25.58
Copy

限制

1s

来源

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

 

理解错题意,以为两条路长度不等,结果就不会做了

结果根本没有这个要求,那么就是单纯的找到一个环,长度最小,环上的点包含全部的点并且每个点只出现一次

有点像借教室那道题

我们可以思考一下,实际上所有的点最后被分成了两部分

那么就可以做DP

f[i][j]表示第一条路走到了i点,第二条路做到了j点,1~j之间的点全部被经过了

那么我们用这个状态更新其他状态,发现下一步只有两种可能,一个是第一条路从i走到了j+1,一个是第二条路从j走到了j+1

然后就好了

 1 #include<bits/stdc++.h>
 2 #define inf 1e30
 3 #define maxn 1000+5
 4 #define maxm 5000000+5
 5 #define eps 1e-10
 6 #define ll long long
 7 #define mod 5000011
 8 #define for0(i,n) for(int i=0;i<=(n);i++)
 9 #define for1(i,n) for(int i=1;i<=(n);i++)
10 #define for2(i,x,y) for(int i=(x);i<=(y);i++)
11 #define for3(i,x,y) for(int i=(x);i>=(y);i--)
12 #define for4(i,x) for(int i=head[x],y=e[i].go;i;i=e[i].next,y=e[i].go)
13 using namespace std;
14 int read(){
15     int x=0,f=1;char ch=getchar();
16     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
17     while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
18     return x*f;
19 }
20 struct poi{
21     int x,y;
22 }a[maxn];
23 int n;
24 double f[maxn][maxn];
25 double cal(int i,int j){
26     double t1=a[i].x-a[j].x,t2=a[i].y-a[j].y;
27     return sqrt(t1*t1+t2*t2);
28 }
29 bool cmp(poi a,poi b){
30     return a.x<b.x;
31 }
32 int main(){
33     //freopen("input.txt","r",stdin);
34     //freopen("output.txt","w",stdout);
35     n=read();
36     for1(i,n)
37         for2(j,i+1,n)
38             f[i][j]=inf;
39     for1(i,n){
40         a[i].x=read();a[i].y=read();
41     }
42     sort(a+1,a+n+1,cmp);
43     f[1][2]=cal(1,2);
44     for1(i,n)
45         for(int j=i+1;j<=n;j++){
46             f[i][j+1]=min(f[i][j+1],f[i][j]+cal(j,j+1));
47             f[j][j+1]=min(f[j][j+1],f[i][j]+cal(i,j+1));
48         }
49     double ans=inf;
50     for1(i,n-1)
51         ans=min(ans,f[i][n]+cal(i,n));
52     printf("%.2lf",ans);
53     return 0;
54 }
View Code

 

转载于:https://www.cnblogs.com/htwx/articles/7294073.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值