Description
You are given an strictly convex polygon with n vertices. It is guaranteed that no three points are collinear. You would like to take a maximum non intersecting path on the polygon vertices that visits each point at most once.
More specifically your path can be represented as some sequence of distinct polygon vertices. Your path is the straight line segments between adjacent vertices in order. These segments are not allowed to touch or intersect each other except at the vertices in your sequence.
Given the polygon, print the maximum length non-intersecting path that visits each point at most once.
Input
The first line of input will contain a single integer nn ( 2<=n<=2500), the number of points.
The next n lines will contain two integers xi,yi (∣xi∣,∣yi∣≤109), denoting the coordinates of the i-th vertex.
It is guaranteed that these points are listed in clockwise order.
Output
Print a single floating point number, representing the longest non-intersecting path that visits the vertices at most once.
Your answer will be accepted if it has absolute or relative error at most 10-910^{-9}
. More specifically, if your answer is aa and the jury answer is bb , your answer will be accepted if $\frac{|a-b|}{max(1,b)}$≤$10^{-9}$.
题目大意
按顺时针给出一个凸多边形,任意两点间有一条直线边,求经过每个点恰好一次的最长不自交路径。
Solution
首先,我们可以注意到,如果从 i 点连线到 j 点,则所有的点被分为两份之间不可以连线。
而对于其中一份,我们必须从 i 点或 j 点出发,按题目要求经过其中的每个点。
不妨设我们现在考虑的是第 i+1 个点到第 j-1 个点,从 i 点出发。
可以发现,我们只有两个选择:
- 连到 i+1,并以该点为起点经过 i+2 到 j-1.
- 连到 j-1,并以该点为起点经过 i+1 到 j-2.
其他情况类似,注意特殊考虑区间跨过 1 号点与 n 号点的情况。
由此,我们可以这样进行dp:
状态:f[i][j] 表示经过第 i 个点到第 j 个点的所有点恰好一次的不自交路径中,以第 i 个点为起点的最长路径。g[i][j] 表示经过第 i 个点到第 j 个点间的所有点恰好一次的不自交路径中,以第 j 个点为起点的最长路径。
转移:f[i][j]=max(dis(i,i+1)+f[i+1][j],dis(i,j)+g[i+1][j])
g[i][j]=max(dis(i,j)+f[i][j-1],dis(j-1,j)+g[i][j-1])
有一个小技巧,可以将环形的信息转化成长度为两倍的链,这样环形的问题就变成了线形的问题。
但是,对于这道题,如果转化为长度两倍的链,那么空间就变为原来的4倍,这将会导致算法常数大大增大。
我们观察到,f[i][j] 只从区间长度是 i-j 的状态转移而来,所以我们可以改一下状态的定义,使得可以使用滚动数组。
定义:f[i][j] 表示经过第 i 个点到第 i+j 个点间的所有点恰好一次的不自交路径中,以第 i 个点为起点的最长路径。g[i][j] 表示经过第 i 个点到第 i+j 个点间的所有点恰好一次的不自交路径中,以第 i+j 个点为起点的最长路径。
转移:f[i][j]=max(dis(i,i+1)+f[i+1][j-1],dis(i,i+j)+g[i+1][j-1])
g[i][j]=max(dis(i,i+j)+f[i][j-1],dis(i+j-1,i+j)+g[i][j-1])
这样,把第二维设为外层阶段,并利用循环数组进行优化,即可省下空间。
code
代码中,f[0]、g[0] 与f[1]、g[1] 互相滚动。
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 const int N=3010; 5 int n; 6 double f[2][N],g[2][N],ans; 7 struct data{ 8 double x,y; 9 }a[N]; 10 double dis(data x,data y){ 11 return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y)); 12 } 13 signed main(){ 14 //freopen(".in","r",stdin); 15 //freopen(".out","w",stdout); 16 scanf("%lld",&n); 17 for(int i=0;i<n;i++) 18 scanf("%lf%lf",&a[i].x,&a[i].y); 19 for(int j=1;j<n;j++) 20 for(int i=0;i<n;i++){ 21 f[j%2][i]=max(f[(j-1)%2][(i+1)%n]+dis(a[i],a[(i+1)%n]),g[(j-1)%2][(i+j)%n]+dis(a[i],a[(i+j)%n])); 22 g[j%2][i]=max(g[(j-1)%2][(i-1+n)%n]+dis(a[i],a[(i-1+n)%n]),f[(j-1)%2][(i-j+n)%n]+dis(a[i],a[(i-j+n)%n])); 23 } 24 for(int i=0;i<n;i++) 25 ans=max(ans,max(f[(n-1)%2][i],g[(n-1)%2][i])); 26 printf("%.10lf\n",ans); 27 return 0; 28 }