背景 Background
欧几里德旅行商(Euclidean Traveling Salesman)问题也就是货郎担问题一直是困扰全世界数学家、计算机学家的著名问题。现有的算法都没有办法在确定型机器上在多项式时间内求出最优解,但是有办法在多项式时间内求出一个较优解。
为了简化问题,而且保证能在多项式时间内求出最优解,J.L.Bentley提出了一种叫做bitonic tour的哈密尔顿环游。它的要求是任意两点(a,b)之间的相互到达的代价dist(a,b)=dist(b,a)且任意两点之间可以相互到达,并且环游的路线只能是从最西端单向到最东端,再单项返回最西端,并且是一个哈密尔顿回路。
描述 Description
著名的NPC难题的简化版本
现在笛卡尔平面上有n(n<=1000)个点,每个点的坐标为(x,y)(-2^31<x,y<2^31,且为整数),任意两点之间相互到达的代价为这两点的欧几里德距离,现要你编程求出最短bitonic tour。
输入格式 Input Format
第一行一个整数n
接下来n行,每行两个整数x,y,表示某个点的坐标。
输入中保证没有重复的两点,
保证最西端和最东端都只有一个点。
输出格式 Output Format
一行,即最短回路的长度,保留2位小数。
非常难想到的DP.
f[i][j] 表示第一个人走到第 i 点,第二个人走到第 j 个点。
因为 f[i][j] 与 f[j][i] 轮换对称, 为避免重复, 规定 i 恒大于 j.
若 j+1=i f[i][j]=min(f[1~j-1][j]+dis(1~j-1,j)); f[j][1~j-1] 与 f[1~j-1][j] 对称。
若 j+1<i f[i][j]=f[i-1][j]+dis(i-1,i);
另外 1e24 表示 10的24次方。
1 #include<iostream>
2 #include<math.h>
3 #include<stdio.h>
4 //#include<fstream>
5 using namespace std;
6 //ifstream fin("cin.in");
7
8 int n;
9 double f[1001][1001];
10 double d[1001][1001];
11 double x[1001],y[1001];
12
13 void Qsort(int left,int right){
14 if(left>=right) return ;
15 int i=left,j=right;double mid=x[(left+right)>>1];
16 while(i<=j)
17 {
18 while(x[i]<mid) i++;
19 while(x[j]>mid) j--;
20 if(i<=j)
21 {
22 swap(x[i],x[j]);
23 swap(y[i],y[j]);
24 i++;j--;
25 }
26 }
27 Qsort(left,j);
28 Qsort(i,right);
29 }
30
31 int main()
32 {
33 cin>>n;
34 for(int i=1;i<=n;++i)
35 cin>>x[i]>>y[i];
36
37 Qsort(1,n);
38
39 for(int i=1;i<=n;++i)
40 for(int j=i+1;j<=n;++j)
41 d[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
42
43 for(int i=1;i<=n;++i)
44 for(int j=1;j<=n;++j)
45 f[i][j]=1e24;
46
47 f[2][1]=d[1][2];
48 for(int i=3;i<=n;++i)
49 for(int j=1;j<i;++j)
50 if(j+1<i) f[i][j]=f[i-1][j]+d[i-1][i];
51 else
52 {
53 for(int k=1;k<j;++k)
54 f[i][j]=min(f[i][j],f[j][k]+d[k][i]);
55 }
56
57 printf("%.2lf\n",f[n][n-1]+d[n-1][n]);
58 return 0;
59
60 }