poj 2079(旋转卡壳)

题意:求出平面内的点集所组成的面积最大的三角形。


解题思路:考虑凸包+旋转卡壳。面积最大的三角形的三点必定在凸包的顶点上,只不过这里要注意,三角形的边不一定就是凸包的边,有可能三角形相邻两点是横跨凸包的。

关键是如何找三个顶点。这里采用的类似于尺取法,先固定一个点,剩下的两个指针依次逆时针方向旋转,找到最大的面积。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

const int maxn = 50000;
const double eps = 1e-8;
struct Point
{
	double x,y;
}p[maxn],Stack[maxn];
int n,top;

double Cross(Point a,Point b,Point c)
{  
    return (a.x-c.x)*(b.y-c.y)-(b.x-c.x)*(a.y-c.y);  
}  

double dis(Point a,Point b)
{  
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);  
}  

int cmp(Point a,Point b)
{  
    if(Cross(a,b,p[0]) > eps) return 1;  
    if(fabs(Cross(a,b,p[0])) < eps && dis(b,p[0]) - dis(a,p[0]) < eps) return 1;  
    return 0;  
}

int Graham()
{
	sort(p+1,p+n,cmp);
	top = 2;  
    Stack[0] = p[0];  
    Stack[1] = p[1];  
    Stack[2] = p[2];
	for(int i = 3; i < n; i++){  
        while(top >= 1 && Cross(p[i],Stack[top],Stack[top-1]) > eps){  
            top--;  
        }  
        Stack[++top]=p[i];  
    } 
	return top;
}

double rotating_calipers()//旋转卡壳
{
    int i,j=1,k=2;
    double ans=0;
    Stack[++top]=Stack[0];
    for(i=0; i<top; i++)
    {
        /*边上的两点可以不是凸包上的点,WA无数次*/
        while(fabs(Cross(Stack[(k+1)%top],Stack[i],Stack[j])) > fabs(Cross(Stack[k],Stack[i],Stack[j])))
            k=(k+1)%top;
        while(fabs(Cross(Stack[k],Stack[i],Stack[(j+1)%top])) > fabs(Cross(Stack[k],Stack[i],Stack[j])))
            j=(j+1)%top;
        ans=max(ans,fabs(Cross(Stack[k],Stack[i],Stack[j])));
    }
    return ans;
}

int main()
{
	while(scanf("%d",&n),n != -1)
	{
		for(int i = 0; i < n; i++)
			scanf("%lf%lf",&p[i].x,&p[i].y);
		int k = 0;  
        for(int i = 1;i < n; i++){  
            if(p[k].y > p[i].y || (p[k].y == p[i].y) && (p[k].x > p[i].x)){  
                k=i;  
            }  
        }  
		swap(p[0],p[k]);
		Graham();
		printf("%.2lf\n",rotating_calipers()/2);
	}
	return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值