题意:按从左到右的顺序给出n个点,求从最左边到最右边再到最左边的最短距离(欧几里德距离)是多少。每个点只能走一次(除了最左边和最右边的点以外)。
思路:
这题在紫书(《算法竞赛入门经典(第2版)》)上作为例题讲过,复杂度是O(n2)。
因为从左到右再回来不方便思考,于是变成两个人同时从最左点出发,走不同的路到达最右点。
为了方便后续的转移,设置dp[i][j]表示1~max(i, j)都走过,且两人的位置分别是i和j时,此时还需要走的距离。
则可以明白dp[i][j]==dp[j][i],所以不妨规定i>j。
那么想走到下一步有两种走法
1.i走到i+1,则有dp[i+1][j]=min(dp[i+1][j], dp[i][j]+dis(i+1, i))
2.j走到i+1,则有dp[i+1][i]=min(dp[i+1][i], dp[i][j]+dis(i+1, j))
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=1010;
struct node
{
double x, y;
}point[maxn];
double dis(int a, int b) //求欧几里德距离
{
double dx=point[a].x-point[b].x;
double dy=point[a].y-point[b].y;
return sqrt(dx*dx+dy*dy);
}
int n;
double dp[maxn][maxn];
int main()
{
while(~scanf("%d", &n))
{
for(int i=0; i<n; i++)
scanf("%lf%lf", &point[i].x, &point[i].y);
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
dp[i][j]=INF;
dp[0][0]=0;
dp[1][0]=dis(1,0);
for(int i=1; i<n; i++)
for(int j=0; j<i; j++)
{
dp[i+1][j]=min(dp[i+1][j], dp[i][j]+dis(i, i+1));
dp[i+1][i]=min(dp[i+1][i], dp[i][j]+dis(i+1, j));
}
double ans=INF*1.0;
for(int i=0; i<n; i++)
ans=min(ans, dp[n-1][i]+dis(n-1, i)); //枚举每个求最小
printf("%.2f\n", ans);
}
return 0;
}