●POJ 1259 The Picnic

 

题链:

http://poj.org/problem?id=1259

题解:

计算几何,凸包,DP

题意:给出N($N\leq100$)个点,求出最大的凸包使得凸包里面不存在点(边上可以有)。输出最大凸包的面积。


把所有点按x从小到大排序(若x相同则按y从小到大排序)。 

依次枚举每个点p,把它作为凸包的左下角来计算此时可以形成的最大凸包。

做法如下:

将p号点作为原点,取出p+1~N的点(共m=N-p个点),对这m个点按极角逆时针排序。

然后定义DP状态:

f[j][i]:表示以i,j作为当前凸壳的最后两个点时的最大面积。(p<j<i)

转移:(S(j,i,p)表示j,i,p三个点形成的三角形)

f[j][i]=max(f[k][j])+S(j,i,p) (p<k<j且 $\vec{kj}\times\vec{ji}\geq0$,即要形成凸壳)

差不多就这样,然后注意细节就好了。(反正我是弄了好久才过样例的,233)

复杂度 $O(N^4)$

 

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 105
#define INF 0x3f3f3f3f
using namespace std;
struct Point{
	int x,y;
	Point(int _x=0,int _y=0):x(_x),y(_y){}
	void Read(){scanf("%d%d",&x,&y);}
}D[MAXN],Q[MAXN];
typedef Point Vector;
bool operator == (Point A,Point B){return A.x==B.x&&A.y==B.y;}
Vector operator - (Point A,Point B){return Vector(A.x-B.x,A.y-B.y);}
int operator ^ (Vector A,Vector B){return A.x*B.y-A.y*B.x;}
int operator * (Vector A,Vector B){return A.x*B.x+A.y*B.y;}
int GL2(Vector A){//Get_Length^2
	return A*A;
}
bool XYcmp(Point A,Point B){return A.x-B.x<0||(A.x-B.x==0&&A.y-B.y<0);}
bool PAcmp(Point A,Point B){return (A^B)>0||((A^B)==0&&GL2(A)<GL2(B));}
bool inTArea(Point P,Point A,Point B){//in_Triangle_Area
	if(((B-A)^(P-A))<=0) return 0;
	if((P^B)<=0||(P^A)>0) return 0;
	return 1;
}
int F[MAXN][MAXN];
int DP(int m){
	int ret=0;
	memset(F,0,sizeof(F));
	for(int i=1;i<=m;i++)
		for(int j=1;j<i;j++)
			for(int k=j+1;k<i;k++){
				if(Q[k]==Q[i]||Q[k]==Q[j]) continue;
				if(!inTArea(Q[k],Q[j],Q[i])) continue;
				F[j][i]=-INF;break;
			}
	for(int i=1;i<=m;i++)
		for(int j=1;j<i;j++) if(!F[j][i]){
			F[j][i]=Q[j]^Q[i]; int tmp=0;
			for(int k=j-1;k;k--){
				if(((Q[j]-Q[k])^(Q[i]-Q[j]))>=0) tmp=max(tmp,F[k][j]);
				if(!(Q[k]^Q[j])) break;
			}
			F[j][i]+=tmp;
			ret=max(ret,F[j][i]);
		}
	return ret;
}
int work(int n){
	int m,ret=0;
	sort(D+1,D+n+1,XYcmp);
	for(int i=1;m=0,i<=n;i++){
		for(int j=i+1;j<=n;j++) Q[++m]=D[j];
		for(int j=1;j<=m;j++) Q[j]=Q[j]-D[i];
		sort(Q+1,Q+m+1,PAcmp);
		ret=max(ret,DP(m));
	}
	return ret;
}
int main(){
	int Case,n; double ans;
	for(scanf("%d",&Case);Case;Case--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++) D[i].Read();
		ans=1.0*work(n)/2;
		printf("%.1lf\n",ans);
	}
	return 0;
}

 

  

 

转载于:https://www.cnblogs.com/zj75211/p/8252489.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值