Date:2022.03.26
题目背景
欧几里德旅行商(Euclidean Traveling Salesman)问题也就是货郎担问题一直是困扰全世界数学家、计算机学家的著名问题。现有的算法都没有办法在确定型机器上在多项式时间内求出最优解,但是有办法在多项式时间内求出一个较优解。
为了简化问题,而且保证能在多项式时间内求出最优解,J.L.Bentley提出了一种叫做bitonic tour的哈密尔顿环游。它的要求是任意两点(a,b)之间的相互到达的代价dist(a,b)=dist(b,a)且任意两点之间可以相互到达,并且环游的路线只能是从最西端单向到最东端,再单项返回最西端,并且是一个哈密尔顿回路。
题目描述
著名的NPC难题的简化版本
现在笛卡尔平面上有n(n<=1000)个点,每个点的坐标为(x,y)(-231<x,y<231,且为整数),任意两点之间相互到达的代价为这两点的欧几里德距离,现要你编程求出最短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
思路:题意就是从起点到终点和从终点到起点的路径组成一个哈密顿回路,即:除了起点和终点外无额外交点。转化模型,从起点走出两条路径到终点,途中两条路径无交叉点(即走不到同一个点)的最短路径和。我们将所有点按横坐标排序,题意保证最左最右两点唯一。
由此,状态方程:
f
[
i
]
[
j
]
:
f[i][j]:
f[i][j]:一条路径走到
i
i
i号点、另一条路径走到
j
j
j号点的最短距离。
初始状态:
f
[
1
]
[
1
]
=
0
;
f[1][1]=0;
f[1][1]=0;
状态转移:
首先,我们要知道
i
、
j
i、j
i、j哪个才是靠后的,因为要考虑最后一个点也就是
k
=
m
a
x
(
i
,
j
)
+
1
k=max(i,j)+1
k=max(i,j)+1要接在
i
i
i还是
j
j
j后面。
①
f
[
i
]
[
k
]
=
f
[
i
]
[
j
]
+
g
[
j
]
[
k
]
;
f[i][k]=f[i][j]+g[j][k];
f[i][k]=f[i][j]+g[j][k];【接在j后面】
②
f
[
k
]
[
j
]
=
f
[
i
]
[
j
]
+
g
[
i
]
[
k
]
;
f[k][j]=f[i][j]+g[i][k];
f[k][j]=f[i][j]+g[i][k];【接在i后面】
注意,其实无需考虑图中是否有
i
=
=
j
i==j
i==j的情况,根本不可能。若
i
=
=
j
i==j
i==j,
k
k
k显然能让这两者不相等。但是注意,最后如果我们要保证答案在
f
[
n
]
[
n
]
f[n][n]
f[n][n],当
k
=
=
n
+
1
k==n+1
k==n+1时也就代表有一条路径到
n
n
n号点了,因此另一条就直接到
n
n
n号点即可。
答案:
f
[
n
]
[
n
]
;
f[n][n];
f[n][n];
代码如下:
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<LL, LL> PII;
const LL N = 1010,INF=0x3f3f3f3f3f3f3f3f;
typedef long long LL;
LL t,n,m,a[N],miny=1e18,maxy=-1e18,l,r;
double dist[N],g[N][N],f[N][N];
struct node
{
LL x,y,idx;
}s[N];
bool st[N];
bool cmp(node a,node b) {return a.x<b.x;}
double dis(LL x1,LL y1,LL x2,LL y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i==j) g[i][j]=0;
else g[i][j]=1000000000000000000.0;
f[i][j]=1000000000000000000.0;
}
for(int i=1;i<=n;i++) cin>>s[i].x>>s[i].y;
sort(s+1,s+1+n,cmp);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
g[i][j]=g[j][i]=dis(s[i].x,s[i].y,s[j].x,s[j].y);
f[1][1]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
//if(i!=j||i==1)
{
LL k=max(i,j)+1;
if(k==n+1)
{
if(j==n) f[n][n]=min(f[n][n],f[i][n]+g[i][n]);
if(i==n) f[n][n]=min(f[n][n],f[n][j]+g[n][j]);
}
else
{
f[k][j]=min(f[k][j],f[i][j]+g[i][k]);
f[i][k]=min(f[i][k],f[i][j]+g[j][k]);
}
}
}
printf("%.2lf",f[n][n]);
return 0;
}