【题目大意】给定n个点,求这n个点围出来的多边形【边数>=3】的最小面积。【如果有三点共线,面积可以为0】
【思路】如果围成的形状多于3边,我们一定可以把它分成若干个三角形。所以如果要最小就必须是三角形。
我先考虑暴力做法:分别枚举3个顶点,O(n^3)。【10分】
然鹅我再思考一下:如果确定了三角形的一条边,我们可以将整个坐标系旋转一下,使这条边成为新的y轴,这时候我们只要找到离这个“y轴”最近的一个点就行了。
【到这我就不会了】
到网上搜一搜,我们发现:可以先预处理一下每两个点之间的直线,记录一下起点终点和斜率。
首先所有点按x从小到大排序。然后直线按斜率从小到大排个序。
当我们处理到直线AB时,AB就成为了y轴,且A,B是相邻的(它们距离y轴距离都为0)(假设A位置在B的上面),离AB最近的点就是AB两侧最近的点。然后到下一条直线时,由于斜率从小到大,所以是把整个图顺时针旋转了一点点,直到下一条直线成为y轴。那么这个时候把A,B在序列中的位置交换一下就行了。因为如果有别的点对相对顺序改变,那么这个点对的斜率一定介于这两条直线之间。下面口胡一下。
【这里的排位是指:在当前的“y轴”下,横坐标从小到大排序所对应的排名】
假设现在我们以l1为y轴。有一组点对,P和Q,它们相对于l1的横坐标分别是a和b,其中a<b。
那么我们一定可以保证PQ的斜率比l1小,因为l1要逆时针旋转一点点才与PQ平行。【相对原坐标系】
然后我们看到比l1斜率更小的第一条直线l2。首先,可以保证,A和B一定在直线l2的同侧。因为如果A和B在l2的异侧,那ABCD这四个点一定可以生成一条斜率介于l1和l2之间的直线【画一画就知道了】,而我们是把斜率排了序,保证了不会有这种情况出现。现在,将整个图再顺时针旋转一点点,使l2成为新的y轴。那么可以保证,A和B的相对顺序一定是改变了的,而且是把A和B的排位交换了一下。【可以脑补一下AB在CD下侧的情况,是同理的】这时候,我们看到PQ,在新的y轴下,P的横坐标是c,Q的横坐标是d,假设c>d,那么PQ的斜率一定比l2的斜率要大,因为PQ逆时针旋转一点点就与l2平行。
对于旋转过后排位会变化的点对【除AB外】,一定会满足PQ的条件:a<b且c>d。但是我们发现它的斜率介于l1和l2之间,然鹅比l1斜率更小的第一条直线是l2,不是PQ,与前提矛盾,所以不存在这样的PQ。
那么就可以保证从l1旋转到l2时,受影响的就只有l1上的两个点。
#include<bits/stdc++.h>
using namespace std;
const double pi=3.14159265358979;
const int maxn=1005;
const int M=1000010;
struct point{
double x,y;
point(double _x=0,double _y=0){x=_x,y=_y;}
friend inline point operator +(const point &a,const point &b){
return point(a.x+b.x,a.y+b.y);
}
friend inline point operator -(const point &a,const point &b){
return point(a.x-b.x,a.y-b.y);
}
friend inline double operator *(const point &a,const point &b){
return (a.x*b.y-a.y*b.x);
}
}p[maxn];
struct line{
int s,t;
double k;
}b[M];
int n,i,j,tot=0;
double ans=1e18;
int id[maxn],pos[maxn];
bool cmp(point a,point b){
if(a.x==b.x) return a.y<b.y;
return a.x<b.x;
}
bool Cmp(line a,line b){
return a.k<b.k;
}
double area(point u,line v){
return fabs((p[v.s]-u)*(p[v.t]-u))*0.5;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%lf%lf",&p[i].x,&p[i].y);
sort(p+1,p+n+1,cmp);
for(int i=1;i<=n;++i) id[i]=pos[i]=i;
for(int i=1;i<=n;++i){
for(int j=i+1;j<=n;++j){
++tot;
b[tot].s=i,b[tot].t=j;
if(p[i].x==p[j].x) b[tot].k=1e18;
else b[tot].k=(p[i].y-p[j].y)/(p[i].x-p[j].x);
}
}
sort(b+1,b+tot+1,Cmp);
//pos[i]存的是p[i]的排名
//id[i]存的是 排名为i的点
for(int i=1;i<=tot;++i){
int j=pos[b[i].s],k=pos[b[i].t];
if(j>k) swap(j,k);
if(j>1) ans=min(ans,area(p[id[j-1]],b[i]));
if(k<n) ans=min(ans,area(p[id[k+1]],b[i]));
if(ans==0) break;
swap(pos[b[i].s],pos[b[i].t]);
swap(id[j],id[k]);
}
printf("%0.2f\n",ans);
}