POJ3246

又一道特么交了十多次才过的题目……

题意是说,给定平面里的n个点(n>=4),从中去除恰好一个点后,求剩下的点所组成凸包面积的最小值。

做法是这样的,先求所有点的一个凸包,设其有m个顶点。显然去除的点一定是这些点其中的一个。取凸包的重心作为基准点对所有的点作极角排序,记录下凸包上每个点出现的位置。考虑到,如果去除了其中一个点,面积的变化一定是凸包上三个相邻的点组成三角形的面积和去除中间的顶点后这个三角形内顶点组成的新的凸包的面积。那么我们就可以枚举每个这样的三角形(也就是枚举凸包上的顶点),求出变化更新即可。注意到以i为中间顶点的三角形只可能包含i-1到i+1这个范围内的点,这样保证了每个点最多只算了两次。但有一种特殊情况是,当重心被包含在某个这样的三角形中时,我们就不能只枚举以上这个范围中的点了,只能暴力算一次所有点的凸包。不过因为这样的三角形不可能有太多个, 所以就算这样暴力枚举时间复杂度也不会太大。

这题的思路应该比较好想,但是一开始我写的程序很乱。理顺思路重写了一次结果好多细节没注意,该枚举凸包顶点的地方搞成了所有顶点,m和n也搞混了几次,最后WA和好多次才过……这种正确率特么怎么敢在正式比赛中开几何啊……编程能力还有待提高啊……

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;

const double eps=1e-9;
double dcmp(double x){if(x<-eps) return -1;return x>eps;}
struct point
{
	double x,y;
	point(double x=0,double y=0):x(x),y(y){}
};
bool cp1(point a,point b) {if(dcmp(a.x-b.x)) return dcmp(a.x-b.x)<0;return dcmp(a.y-b.y)<0;}
bool operator ==(point a,point b){return !dcmp(a.x-b.x)&&!dcmp(a.y-b.y);}
point operator +(point a,point b){return point(a.x+b.x,a.y+b.y);}
point operator -(point a,point b){return point(a.x-b.x,a.y-b.y);}
point operator *(point a,double b){return point(a.x*b,a.y*b);}
point operator /(point a,double b){return point(a.x/b,a.y/b);}
double cross(point a,point b){return a.x*b.y-a.y*b.x;}
double dot(point a,point b){return a.x*b.x+a.y*b.y;}
double length(point a){return sqrt(dot(a,a));}
double polyarea(point *p,int n)
{
	if(n<3) return 0;
	double area=0;
	for(int i=1;i<n-1;i++) area+=cross(p[i]-p[0],p[i+1]-p[0]);
	return fabs(area/2);
}
int convexhull(point *p,int n,point *ch)
{
	sort(p,p+n,cp1);
	int m=0;
	for(int i=0;i<n;i++)
	{
		while(m>1&&dcmp(cross(ch[m-1]-ch[m-2],p[i]-ch[m-2]))<=0) --m;
		ch[m++]=p[i];
	}
	int k=m;
	for(int i=n-2;i>=0;i--)
	{
		while(m>k&&dcmp(cross(ch[m-1]-ch[m-2],p[i]-ch[m-2]))<=0) --m;
		ch[m++]=p[i];
	}
	if(n>1) --m;
	return m;
}
point wi;
bool cp2(point a,point b){a=a-wi;b=b-wi;double te1=atan2(a.y,a.x),te2=atan2(b.y,b.x);return te1<te2;}
bool onseg(point p,point a,point b)
{
	return dcmp(cross(a-p,b-p))==0&&dcmp(dot(a-p,b-p))<0;
}
bool isintri(point p,point a,point b,point c)
{
	if(p==a||p==b||p==c) return false;
	if(onseg(p,b,c)) return false;
	if(onseg(p,a,b)||onseg(p,a,c)) return true;
	if(dcmp(cross(b-a,p-a))<0||dcmp(cross(c-b,p-b))<0||dcmp(cross(a-c,p-c))<0) return false;
	return true;
}
point p[200005],ch[200005],pt[200005],qt[200005];
int ptn,qtn;
int n,m;
int beg[200005];

int main()
{
	while(scanf("%d",&n)!=EOF&&n)
	{
		int i,j;
		wi=point();
		for(i=0;i<n;i++) scanf("%lf %lf",&p[i].x,&p[i].y);
		m=convexhull(p,n,ch);
		//cout<<m<<endl;
		if(m<3) {printf("0.00\n");continue;}
		for(i=0;i<m;i++) wi=wi+ch[i];wi=wi/m;
		if(fabs(wi.x-(int)wi.x)<eps) wi.x+=1e-7;
		double art=polyarea(ch,m),res=art,rest;
		sort(p,p+n,cp2);
		for(i=j=0;i<m;j=(j+1)%n) if(ch[i]==p[j]) {beg[i++]=j;}
		for(i=0;i<m;i++)
		{
			ptn=0;pt[ptn++]=ch[i];pt[ptn++]=ch[(i+2)%m];
			if(isintri(wi,ch[(i+1)%m],ch[(i+2)%m],ch[i]))
			{
				ptn=0;for(j=0;j<n;j++) if(!(p[j]==ch[(i+1)%m])) pt[ptn++]=p[j];
				qtn=convexhull(pt,ptn,qt);res=min(res,polyarea(qt,qtn));
				continue;
			}
			for(j=beg[i];j!=beg[(i+2)%m];j=(j+1)%n)
				if(isintri(p[j],ch[(i+1)%m],ch[(i+2)%m],ch[i]))
					pt[ptn++]=p[j];
			//for(j=0;j<ptn;j++) cout<<pt[j].x<<" "<<pt[j].y<<", ";cout<<endl;
			qtn=convexhull(pt,ptn,qt);
			res=min(res,polyarea(qt,qtn)+art-fabs(cross(ch[(i+1)%m]-ch[i],ch[(i+2)%m]-ch[i]))/2);
		}
		printf("%.2lf\n",res);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值