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;
}