[题解]P4166 [SCOI2007] 最大土地面积

P4166 [SCOI2007] 最大土地面积

解法\(1\) - \(O(n^2)\)

我们运用调整法,可以证明这个四边形的\(4\)个顶点一定都在凸包的顶点上,具体来说:

\(\textbf{Proof:}\)
首先我们知道,凸包内,到某条直线距离最大的点一定包括\(1\)个顶点。
接下来我们考虑一个凸包内的四边形:

我们过它的对角线做一条直线\(JL\),然后我们就根据上面的结论知道,到\(JL\)距离最大的点一定是凸包的某一个顶点。因此经过调整,把\(M\)\(K\)都调整到顶点的位置,距离增大了,自然面积也增大。

同理,一直调整到所有四边形顶点都在凸包的顶点上,我们就证明了“不是所有顶点都在凸包顶点上的四边形,面积一定不是最大”,即证明了“面积最大的四边形一定所有顶点都在凸包的顶点上”。\(\color{#f0f0f0}\textbf{Q.E.D.}\)

接下来我们就可以枚举凸包的顶点来解决了,但最暴力的方法\(O(n^4)\)会超时,想想怎么优化。

受旋转卡壳的启发,我们想到枚举一条对角线,然后找\(2\)个离这条对角线最远的点,分别位于对角线的两侧。和旋转卡壳相同地,这一步也是可以用双指针的思想优化的。

#1
#include<bits/stdc++.h>
#define nxtnode(x) (x%top+1)
#define N 2010
using namespace std;
struct vec{
	double x,y;
	vec operator+(vec b){return {x+b.x,y+b.y};}
	vec operator-(vec b){return {x-b.x,y-b.y};}
}a[N];
bool cmp(vec a,vec b){return a.x==b.x?a.y<b.y:a.x<b.x;}
double cross(vec a,vec b){return a.x*b.y-a.y*b.x;}
int n,st[N],top;
bool vis[N];
double ans;
void convex_hull(){
	sort(a+1,a+1+n,cmp);
	memset(vis,0,sizeof vis);
	st[top=1]=1;
	for(int i=2;i<=n;i++){
		while(top>1&&cross(a[st[top]]-a[st[top-1]],a[i]-a[st[top]])>=0){
			vis[st[top--]]=0;
		}
		vis[i]=1,st[++top]=i;
	}
	int tmp=top;
	for(int i=n-1;i>=1;i--){
		if(vis[i]) continue;
		while(top!=tmp&&cross(a[st[top]]-a[st[top-1]],a[i]-a[st[top]])>=0){
			vis[st[top--]]=0;
		}
		vis[i]=1,st[++top]=i;
	}
	top--;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i].x>>a[i].y;
	convex_hull();
	int p1,p2;
	for(int i=1;i<top;i++){
		p1=p2=i;
		for(int j=i+1;j<=top;j++){
			while(cross(a[st[j]]-a[st[i]],a[st[nxtnode(p1)]]-a[st[i]])>=cross(a[st[j]]-a[st[i]],a[st[p1]]-a[st[i]])){
				p1=nxtnode(p1);
			}
			while(cross(a[st[j]]-a[st[i]],a[st[nxtnode(p2)]]-a[st[i]])<=cross(a[st[j]]-a[st[i]],a[st[p2]]-a[st[i]])){
				p2=nxtnode(p2);
			}
			ans=max(ans,cross(a[st[j]]-a[st[i]],a[st[p1]]-a[st[i]])-cross(a[st[j]]-a[st[i]],a[st[p2]]-a[st[i]]));
		}
	}
	cout<<fixed<<setprecision(3)<<fabs(ans)/2<<"\n";
	return 0;
}

解法\(2\) - \(O(n\log n)\)

进一步分析,我们可以发现,这个面积最大的四边形的对角线一定是对踵点的连线。

\(\textbf{Proof:}\)
我们假设对角线不全是对踵点的连线:

连接\(CG\),则一定能找到到\(CG\)上面离它更远的点,使得面积更大。

此时\(J\)\(E\)都离\(CG\)最远,无法继续调整,它们是对踵点,

所以我们可以想到一个\(O(n)\)的做法来完成这一过程。

仅需枚举一个顶点\(U\),然后找到它的对踵点\(V\),再找到\(UV\)两侧,离\(UV\)最远的两点\(A,B\)。每次更新答案即可。

根据\(U\)\(V\)可以用双指针的思想来优化,和旋转卡壳枚举点的做法相似。相应地,我们发现线段\(UV\)是按一个方向旋转的,所以\(A,B\)的枚举也可以双指针。

注意第\(1\)个步骤如果出现共线的情况,两个点都需要考虑,不过注意顺序(\(50,51\)行),否则不能保证\(UV\)按一个方向旋转,导致出现错误,会被4 -15 -4 4 13 15 4 -4 -13卡掉(虽然交上去会A)。

这一步骤是\(O(n)\)的,加上凸包的\(O(n\log n)\),总时间复杂度\(O(n\log n)\)

#2
#include<bits/stdc++.h>
#define nxtnode(x) (x%top+1)
#define N 50010
using namespace std;
struct vec{
	double x,y;
	vec operator+(vec b){return {x+b.x,y+b.y};}
	vec operator-(vec b){return {x-b.x,y-b.y};}
	int squalen(){return x*x+y*y;}
}a[N];
bool cmp(vec a,vec b){return a.x==b.x?a.y<b.y:a.x<b.x;}
double cross(vec a,vec b){return a.x*b.y-a.y*b.x;}
int n,st[N],top;
double ans;
void convex_hull(){
	sort(a+1,a+1+n,cmp);
	st[top=1]=1;
	for(int i=2;i<=n;i++){
		while(top>1&&cross(a[st[top]]-a[st[top-1]],a[i]-a[st[top]])>=0)
			top--;
		st[++top]=i;
	}
	int tmp=top;
	for(int i=n-1;i>=1;i--){
		while(top!=tmp&&cross(a[st[top]]-a[st[top-1]],a[i]-a[st[top]])>=0)
			top--;
		st[++top]=i;
	}
	top--;
}
void modify(int i,int j,int &k,int &l){//k从i开始,l从j开始
	double tk,tl;
	while(cross(a[st[j]]-a[st[i]],a[st[nxtnode(k)]]-a[st[i]])>=(tk=cross(a[st[j]]-a[st[i]],a[st[k]]-a[st[i]])))
		k=nxtnode(k);
	while(cross(a[st[j]]-a[st[i]],a[st[nxtnode(l)]]-a[st[i]])<=(tl=cross(a[st[j]]-a[st[i]],a[st[l]]-a[st[i]])))
		l=nxtnode(l);
	ans=max(ans,(tk-tl)/2);
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i].x>>a[i].y;
	convex_hull();//顺时针
	int j=2,k=1,l=2;
	for(int i=1;i<=top;i++){
		double t;
		while((t=cross(a[st[nxtnode(i)]]-a[st[i]],a[st[nxtnode(j)]]-a[st[j]]))<0){
			if(l==j) l=nxtnode(l);
			j=nxtnode(j);
		}
		modify(i,j,k,l);
		if(t==0) modify(i,nxtnode(j),k,l);//两句不能反
	}
	cout<<fixed<<setprecision(3)<<ans<<"\n";
	return 0;
}
posted @ 2024-07-20 21:45  Sinktank  阅读(37)  评论(0编辑  收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2024 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.