JZOJ 5268. 旅行

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[n1][j]=dist[n1][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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值