题目大意:
给定平面上n个点的坐标,进行一次遍历,遍历的规则就是先从最左边的点遍历到最右边的点,然后从最右边的点遍历到最左边的点,而且所有点要遍历完。
算法:这题完全就是双调旅途的模板
求解过程:
(1)首先将各点按照x坐标从小到大排列,时间复杂度为O(nlgn)。
(2)寻找子结构:定义从Pi到Pj的路径为:从Pi开始,从右到左一直到P1,然后从左到右一直到Pj。在这个路径上,会经过P1到Pmax(i,j)之间的所有点且只经过一次。
在定义d(i,j)为满足这一条件的最短路径。我们只考虑i>=j的情况。
同时,定义dist(i,j)为点Pi到Pj之间的直线距离。
(3)最优解:我们需要求的是d(n,n)。
关于子问题d(i,j)的求解,分三种情况:
A、当j < i - 1时,d(i,j) = d(i-1,j) + dist(i - 1,i)。
由定义可知,点Pi-1一定在路径Pi-Pj上,而且又由于j<i-1,因此Pi的左边的相邻点一定是Pi-1.因此可以得出上述等式。
B、当j = i - 1时,与Pi左相邻的那个点可能是P1到Pi-1总的任何一个。因此需要递归求出最小的那个路径:
d(i,j) = d(i,i-1) = min{d(k,j) + dist(i,k)},其中1 <= k <= j。
C、当j=i时,路径上最后相连的两个点可能是P1-Pi、P2-Pi...Pi-1-Pi。
因此有:
d(i,i) = min{d(i,1)+dist(1,i),...,d(i,i-1),dist(i-1,i)}.。
#include <set>
#include <map>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <iostream>
#include <algorithm>
using namespace std;
#define L(i) i<<1
#define R(i) i<<1|1
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-9
#define maxn 1000010
#define MOD 1000000007
#define zero(x) (((x)>0?(x):-(x))<eps)
int n;
int x[220],y[220];
double dis[220][220];
double dp[220][220];
int main()
{
while(scanf("%d",&n) != EOF)
{
for(int i = 1; i <= n; i++)
scanf("%d%d",&x[i],&y[i]);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
dis[i][j] = sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
dp[2][1] = dis[1][2];
for(int i = 3; i <= n; i++)
for(int j = 1; j < i; j++)
{
if(i - 1 > j)
dp[i][j] = dp[i-1][j] + dis[i-1][i];
else
{
dp[i][j] = 10000000;
for(int k = 1; k < j; k++)
dp[i][j] = min(dp[i][j],dp[j][k]+dis[k][i]);
}
}
dp[n][n] = dp[n][n-1] + dis[n][n-1];
printf("%.2f\n",dp[n][n]);
}
return 0;
}