HDU 2202 最大三角形 (凸包)

Description

老师在计算几何这门课上给Eddy布置了一道题目,题目是这样的:给定二维的平面上n个不同的点,要求在这些点里寻找三个点,使他们构成的三角形拥有的面积最大。

Eddy对这道题目百思不得其解,想不通用什么方法来解决,因此他找到了聪明的你,请你帮他解决这个题目。

Input

输入数据包含多组测试用例,每个测试用例的第一行包含一个整数n,表示一共有n个互不相同的点,接下来的n行每行包含2个整数xi,yi,表示平面上第i个点的x与y坐标。你可以认为:3 <= n <= 50000 而且 -10000 <= xi, yi <= 10000.

Output

对于每一组测试数据,请输出构成的最大的三角形的面积,结果保留两位小数。

每组输出占一行。

Sample Input

3
3 4
2 6
3 7
6
2 6
3 9
2 0
8 0
6 6
7 7

Sample Output

1.50
27.00

数据量过大如果直接暴力枚举三个点面积取最大会超时
因为最大三角形的顶点一定在凸包上,所以可以用graham扫描线求凸包缩小数据量再枚举
graham扫描线法
  1. 取得最左下的点的下标,并把其与第一个位置的点交换
  2. 以第一个点为原点水平线为x轴,进行极角排序(或者说与原点连线的斜率排序)
  3. graham扫描线(栈的思想 不过下面的代码用的是数组)
    • 第一个点和第二个点肯定在凸包上
    • 对后续点进行遍历(设为点 x)
      • 取栈顶两个点连线 l
      • 如果 x在l的右边则把栈顶元素出栈(while循环)
      • x入栈

最后得到的栈中的点集就是凸包的点集

极点排序(叉积)

在这里插入图片描述

三个点构成的三角形面积

叉积是两个向量所构成的平行四边形的有向面积

因为可能会有多次出栈操作,所以用while循环

P3 -> P4 -> P5 -> P6 -> P5出栈 -> P4出栈

在这里插入图片描述

代码

#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#include<math.h>
#include<stack>
#include<vector>
#include<stdio.h>
#define ll long long
#define inf 0x3f3f3f3f
#define pi 3.1415926535
using namespace std;
struct node
{
    double x,y;
};
node w[50005],v[50005];
int n,t;
bool cmp(node a,node b)//极角排序(叉积)
{
    if((a.x-w[0].x)*(b.y-w[0].y)-(a.y-w[0].y)*(b.x-w[0].x)==0)
        return a.x<b.x;
    return (a.x-w[0].x)*(b.y-w[0].y)-(a.y-w[0].y)*(b.x-w[0].x)>0;
}
double cj(node a,node b,node c)//叉积  A=a-c    B=b-c(A和B呈右手系返回值>0左手系<0共线=0)
{
    return (a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x);
}
void graham()//graham扫描线求凸包
{
    t=1;
    v[0]=w[0],v[1]=w[1];
    for(int i=2; i<n; ++i)
    {
        while(t>1&&cj(w[i],v[t],v[t-1])>=0)
            --t;
        v[++t]=w[i];
    }
}
int main()
{
    //freopen("text.txt","r",stdin);
    while(~scanf("%d",&n))
    {
        t=0;
        for(int i=0; i<n; ++i)
        {
            scanf("%lf %lf",&w[i].x,&w[i].y);
            if(w[i].y<w[t].y||(w[i].y==w[t].y&&w[i].x<w[t].x))
                t=i;
        }
        swap(w[0],w[t]);//将最左下角的点放在第一个位置
        sort(w+1,w+n,cmp);//以第一个点为原点对面的点进行极角排序
        graham();
        double ans=0;
        for(int i=0; i<=t; ++i)
            for(int j=i+1; j<=t; ++j)//从0开始枚举会与前面的有重复
                for(int k=j+1; k<=t; ++k)
                    if(ans<fabs(cj(v[i],v[j],v[k])))
                        ans=fabs(cj(v[i],v[j],v[k]));
        if(t>=2)
            printf("%.2f\n",ans/2);
        
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值