题目描述
在某块平面土地上有N个点,你可以选择其中的任意四个点,将这片土地围起来,当然,你希望这四个点围成
的多边形面积最大。
N<=2000
分析:
比较基础的计算几何代码复习题(误)
首先,很容易证明一点:
所选择的点一定位于凸包上。
因为如果存在最大四边形使得点不全在凸包上,可以将不在凸包上的点外延,得到更大的凸包:
如下图:
所选择的四边形中:左上角的点不在凸包上,那么如果那个点选择黄色区域的任意一点,都可以得到一个更大的凸包:
很显然,这个四边形就不可能是最大的。
得到这个性质以后,我们选择的范围大大缩小,不过数量级没有变化。
再来研究求解的过程:
一种很巧妙的思路是,暴力枚举两个凸包上的点作为四边形的对角线,再在这条线两侧各选择一个点,使得该点与这条线所构成的三角形面积尽量大。
这样做的优点是:可以将问题分解为两个互不干扰的相同的子问题,使问题变得简单。
接下来有两种方法:
Solution A:
不难发现,如果要在某一侧选一个点,使得该点与这条线构成的三角形面积最大:假设这条线为底,因为这条线的长度已经确定,我们只需要使得它的高尽量大即可。
很容易发现:在凸包上,这样的点组成的高线长是一个单峰函数。所以只要在这些点中三分,就可以在
O(log n)
O
(
l
o
g
n
)
复杂度内找到最远点。
总的复杂度为
O(n2log n)
O
(
n
2
l
o
g
n
)
//不过我写都没写这种方法。
Solution B
继续研究这个单峰函数:
假设:能够使枚举的对角线在有一公共点的情况下有序枚举(如图)。
在这种情况下,结合之前的结论(单峰函数),我们会发现一点:对于这些线,所有的波峰的取值也是有序的(如图)
对应关系:
1-D,2-D,3-E,4-F,5-F
有了这个性质,我们就不用再三分了。
我们根据上一次的波峰,比较它与它靠后一个点,与新的线所组成的三角形面积的大小。这样一来,复杂度也降低了,总的复杂度:
O(n2)
O
(
n
2
)
这个思路看似十分巧妙,其实是根据旋转卡壳的实现思路转化而来的,如果看作是为旋转卡壳作思想准备,这是一道不错的好题。
除此之外,这就是一道不折不扣的计算几何代码复习题了
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 2010
using namespace std;
struct node{
double x,y;
node(){}
node(double xx,double yy):x(xx),y(yy) {}
node operator + (const node &a) const {
return node(x+a.x,y+a.y);
}
node operator - (const node &a) const {
return node(x-a.x,y-a.y);
}
node operator * (const double &t) const {
return node(x*t,y*t);
}
double operator *(const node &a) const{
return x*a.x+y*a.y;
}
double operator ^(const node &a) const{
return x*a.y-y*a.x;
}
bool operator < (const node &a) const{
return x<a.x||(x==a.x&&y<a.y);
}
}p[MAXN],l1[MAXN];
stack<node> s1,s;
double ans;
int n,cnt1;
void solve(node a1[],int &cnt){
s.push(p[1]);
s1.push(p[1]);
s1.push(p[2]);
for(int i=3;i<=n;i++){
while(!s.empty()&&((p[i]-s.top())^(s1.top()-s.top()))<=0){
s.pop();
s1.pop();
}
s.push(s1.top());
s1.push(p[i]);
}
while(!s1.empty()){
a1[++cnt]=s1.top();
s1.pop();
}
while(!s.empty())
s.pop();
}
double sum(node a,node b,node c){
return fabs((b-a)^(c-a));
}
bool cmp(node a,node b){
return b<a;
}
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
SF("%d",&n);
for(int i=1;i<=n;i++)
SF("%lf%lf",&p[i].x,&p[i].y);
sort(p+1,p+1+n);
solve(l1,cnt1);
sort(p+1,p+1+n,cmp);
cnt1--;
solve(l1,cnt1);
cnt1--;
/*for(int i=1;i<=cnt1;i++)
PF("{%.2f %.2f}\n",l1[i].x,l1[i].y);*/
/*node t1=node(0,0);
node t2=node(1,1);
node t3=node(2,0);
PF("{%.3f}\n",sum(t1,t2,t3));*/
for(int i=1;i<=cnt1;i++){
int st1=i%cnt1+1;
int j=(i+1)%cnt1+1;
int st2=(j+1)%cnt1+1;
for(;j%cnt1+1!=i;j=j%cnt1+1){
while(st1%cnt1+1!=j&&sum(l1[i],l1[st1],l1[j])<sum(l1[i],l1[st1%cnt1+1],l1[j]))
st1=st1%cnt1+1;
while(st2%cnt1+1!=i&&sum(l1[i],l1[st2],l1[j])<sum(l1[i],l1[st2%cnt1+1],l1[j]))
st2=st2%cnt1+1;
//PF("[%d(%.0f,%.0f) %d(%.0f,%.0f)-%d(%.0f,%.0f) %d(%.0f,%.0f) %.2f %.2f]\n",i,l1[i].x,l1[i].y,st1,l1[st1].x,l1[st1].y,st2,l1[st2].x,l1[st2].y,j,l1[j].x,l1[j].y,sum(l1[i],l1[st1],l1[j])/2.0,sum(l1[i],l1[st2],l1[j])/2.0);
ans=max(ans,(sum(l1[i],l1[st1],l1[j])+sum(l1[i],l1[st2],l1[j]))/2.0);
}
}
PF("%.3lf",ans);
}