洛谷 CF838E Convex Countour

题目传送门

考虑“除了路径序列中的顶点外,这些线段不能相交或接触”是什么意思。画一下图:

路径不能出现如上图的交叉。再天马行空地画几种合法的路径:

只要稍加观察就会发现,第一张图中的路径一定不会是答案,因为 \(1\rightarrow 8\rightarrow 6\) 优于 \(1\rightarrow 6\),同时 \(8\rightarrow 7\rightarrow 6\) 优于 \(8\rightarrow 6\)。而对于第二张图中的路径,貌似就不太能找到显然优于它的。

进一步地,我们发现 最优的路径一定会经过多边形的所有顶点。不妨再以上面两张合法的图为例。在第一张图中,蓝色路径没有经过点 \(7,8\),又因为路径不能相交,所以外面的点(\(2,3,4,5\))也不能与 \(7,8\) 连。由于三角形两边之和大于第三边,所以 \(1\rightarrow 6\) 一定劣于 \(1\rightarrow 8 \rightarrow 7\rightarrow 6\)。第二张图就没有类似的问题。

因为要经过所有的点恰好一次,再画一画就知道不可能出现下面的情况:

\(1\rightarrow 3\rightarrow 6\),到 \(6\) 之后,无论往左还是往右,之后都不能覆盖另一边的所有点。要避免这种情况,只能在从 \(i\) 走到 \(j\) 后,下一步走到与 \(i\)\(j\) 相邻的点

所以最优路径中,已经经过的所有点是连续的,且每次只能向外扩展 \(1\) 个位置。因此断环成链,设计区间 DP:状态需要区分路径方向,设 \(dp_{l,r,0/1}\) 表示区间 \([l,r]\) 内的答案,且 \(l/r\) 为终点。初始 \(dp_{i,i,0}=dp_{i,i,1}=0\),有转移

\[dp_{l,r,0}=\max\{dp_{l+1,r,0}+dis(l,l+1),dp_{l+1,r,1}+dis(l,r)\} \]

\[dp_{l,r,1}=\max\{dp_{l,r-1,0}+dis(r-1,r),dp_{l,r-1,1}+dis(l,r)\} \]

最终答案为

\[\max\limits_{l=1}^{n}\max\{dp_{l,l+n-1,0},dp_{l,l+n-1,1}\} \]

然而 \(5000\times 5000\times 2\)double 数组爆了空间。需要换一种状态设计:设 \(dp_{len,l,0/1}\) 表示区间长度为 \(len\),左端点为 \(l\)\(l\) 为终点/起点时,\([l,r]\) 内的答案。转移是类似的。由于求 \(dp_{len}\) 只需要 \(dp_{len-1}\) 和坐标,所以可以滚动数组优化。时间复杂度 \(\mathcal{O}(n^2)\),空间复杂度 \(\mathcal{O}(n)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
bool Mbegin;
void File_Work(){
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
}
typedef double Ld;
const int N=5e3+8;
int n;
Ld xx[N],yy[N],dp[2][N][2];
Ld get_dis(int i,int j){
	return sqrt((xx[i]-xx[j])*(xx[i]-xx[j])+(yy[i]-yy[j])*(yy[i]-yy[j]));
} 
void MAIN(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>xx[i]>>yy[i];
		xx[i+n]=xx[i],yy[i+n]=yy[i];
	}
	for(int len=2;len<=n;len++){
		for(int l=1;l+len-1<=n*2;l++){
			int r=l+len-1;
			dp[len&1][l][0]=max(dp[!(len&1)][l+1][0]+get_dis(l,l+1),dp[!(len&1)][l+1][1]+get_dis(l,r));
			dp[len&1][l][1]=max(dp[!(len&1)][l][1]+get_dis(r-1,r),dp[!(len&1)][l][0]+get_dis(l,r));
		}
	}
	Ld ans=0;
	for(int l=1;l<=n;l++)
		ans=max({ans,dp[n&1][l][0],dp[n&1][l][1]});
	cout<<fixed<<setprecision(10)<<ans;
}
bool Mend;
int main(){
//	File_Work();
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cerr<<fixed<<setprecision(3)<<(&Mbegin-&Mend)/1048576.0<<" MB\n\n\n";
	int testid,testnum=1;
//	cin>>testid;
//	cin>>testnum;
	while(testnum--)
		MAIN();
	cerr<<"\n\n\n"<<fixed<<setprecision(0)<<1e3*clock()/CLOCKS_PER_SEC<<" ms";
	return 0;
}
posted @ 2026-07-09 16:43  PenguinNatori  阅读(4)  评论(0)    收藏  举报