P1523 旅行商简化版 题解

题目传送门

在确定方向后,这道题也就不再是 NPC 问题了,而是询问从一个起点出发的,具有相同终点的两条路径的最小总长度。由此想到 DP 做法,且与 P1006 有相像之处。

不妨设 \(f[i][j]\) 为一个点走到 \(i\) 位置,一个点走到 \(j\)​ 位置(由于 \(f[i][j]=f[j][j]\),所以不妨令 \(i>j\)​)时,两点距离终点的最短距离。

但是如果一个人往 \(i+2\)​ 走,中间会少 \(i+1\)​​ 这个点没被走过,无法表示成状态。那么我们可以限制只让走 \(i+1\)​ 来解决这一问题。

由此有状态转移方程:

\[f_{x,y}=\min(f_{x+1,y}+dis(x,x+1),f_{x,x+1}+dis(y,x+1)) \]

其中 \(dis(x,y)\) 指点 \(x\)\(y\) 的距离,用公式 \(\sqrt{x^2+y^2}\) 计算。

在最后输出 \(f_{2,1}+dis(1,2)\) 即可。​

#include<bits/stdc++.h>
using namespace std;

//#define int long long
#define ll long long
#define ri register int
#define il inline

const int INF=0x7fffffff,N=1e3+10;
int n;
double d1,d2;
double f[N][N];
struct pts{
    double x,y;
}p[N];

il ll read(){
    ll x=0,y=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')
            y=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=x*10+c-'0';
        c=getchar();
    }
    return x*y;
}

il bool cmp(pts x,pts y){
    return x.x<y.x;
}

il double dis(double x_1,double y_1,double x_2,double y_2){
	return sqrt((x_1-x_2)*(x_1-x_2)+(y_1-y_2)*(y_1-y_2));
}

signed main(){
    n=read();
    for(ri i=1;i<=n;i++)
        scanf("%lf%lf",&p[i].x,&p[i].y);
    sort(p+1,p+n+1,cmp);
    d1=dis(p[n].x,p[n].y,p[n-1].x,p[n-1].y),d2=dis(p[1].x,p[1].y,p[2].x,p[2].y);
	for(ri i=1;i<=n-2;i++)
        f[n-1][i]=dis(p[n].x,p[n].y,p[i].x,p[i].y)+d1;
    for(ri i=n-2;i>=2;i--){
		for(ri j=1;j<i;j++){
			f[i][j]=f[i+1][j]+dis(p[i].x,p[i].y,p[i+1].x,p[i+1].y);
			f[i][j]=min(f[i][j],f[i+1][i]+dis(p[j].x,p[j].y,p[i+1].x,p[i+1].y));
		}
	}
	printf("%.2lf",f[2][1]+d2);
    return 0;
}
posted @ 2021-07-29 22:37  BFNewdawn  阅读(59)  评论(0编辑  收藏  举报