Description
给定平面上n个点,设计一条路线,从最左边的点出发,走到最右边的点在走回来,除了最左边的点,其他每个点恰好经过一次,且是的路径总长最短。两点之间的路径长度为欧几里得距离(就是直线距离)。
Input
第一行一个整数n表示点数,后面n行,每行两个小数表示点的坐标。
Output
一个小数表示最短的路径总长,答案保留4位小数。
Sample Input
5
10 10
2 8
1 2
10 6
2 5
Sample Output
29.5535
Data Constraint
对于40%数据n<=100
对于100%数据n<=5000
Solution
这道题的关键就是如何处理来回走、且走的点不重复的问题。
于是可以巧妙的改成:两个人同时从最左点出发,沿着两条不同的路径走,最后都到达最右点,
且除了起点和终点外其余每个点恰好被一个人经过。
则设出DP状态: F[i][j] 表示从 1 到
Max(i,j) 都已被走过、且不重复的最小距离。那么走出下一步(倒着做), F[i][j] 可以由 F[i+1][j] 和 F[i][j+1] 转移过来。
由于两个人是完全相同的,所以强制 i>j , 则有: F[i][j]=F[j][i]
于是 F[i][j] 就由 F[i+1][j] 和 F[i+1][i] 转移过来。
且边界是: F[n−1][j]=dist[n−1][n]+dist[j][n] (直接 i,j 各走一步到终点即可)
那么答案就是: dist[1][2]+F[2][1] 。
(因为第一步是 i 走到了第二个点,根据定义就是
F[2][1] )这样时间复杂度就是 O(N2) ,注意空间问题(开滚动数组解决)和精度问题(开 double 解决)。
Code
#include<cstdio>
#include<cmath>
using namespace std;
const int N=5001;
struct data
{
double x,y;
}a[N];
int n,roll;
double f[2][N];
inline double get(int x,int y)
{
return sqrt((a[x].x-a[y].x)*(a[x].x-a[y].x)+(a[x].y-a[y].y)*(a[x].y-a[y].y));
}
inline double min(double x,double y)
{
return x<y?x:y;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y);
for(int i=1;i<n-1;i++) f[0][i]=get(n-1,n)+get(i,n);
for(int i=n-2;i>1;i--)
{
roll^=1;
for(int j=1;j<i;j++)
f[roll][j]=min(f[roll^1][j]+get(i,i+1),f[roll^1][i]+get(j,i+1));
}
printf("%.4lf",f[roll][1]+get(1,2));
return 0;
}