1. 程式人生 > 其它 >P1523 旅行商簡化版 題解

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