【C++ | PTA】神坛

文章描述了一个编程问题,要求计算一组神石中选出3个形成神坛的最小面积。这个问题转化为计算二维平面点集的最小凸包。通过排序点的单位向量并构建凸包,找到最小面积的三角形。提供的C++代码实现了解决方案,涉及点的坐标、向量运算和排序策略。
摘要由CSDN通过智能技术生成

题目要求

在古老的迈瑞城,巍然屹立着 n 块神石。长老们商议,选取 3 块神石围成一个神坛。因为神坛的能量强度与它的面积成反比,因此神坛的面积越小越好。特殊地,如果有两块神石坐标相同,或者三块神石共线,神坛的面积为 0.000。

长老们发现这个问题没有那么简单,于是委托你编程解决这个难题。

输入格式:
输入在第一行给出一个正整数 n(3 ≤ n ≤ 5000)。随后 n 行,每行有两个整数,分别表示神石的横坐标、纵坐标(−109≤ 横坐标、纵坐标 <109)。

输出格式:
在一行中输出神坛的最小面积,四舍五入保留 3 位小数。

输入样例:
8
3 4
2 4
1 1
4 1
0 3
3 0
1 3
4 2

输出样例:
0.500

样例解释:
输出的数值等于图中红色或紫色框线的三角形的面积。

在这里插入图片描述


解题前提

该程序可归类为计算平面内点集的最小面积的凸包

凹包算法定义:对于平面内的一组点,凸包是指这组点的凸多边形中,所有的顶点都是这组点中的点的集合。

凸包(Convex Hull)算法是一种计算二维平面点集最小凸多边形的算法。而凸多边形具有如下特征:在其内部的任意两点,连线都必须位于多边形内部。

因此,凹包(Concave Hull)问题就是将给定的点集包围在一个凹多边形中的算法。凹包问题的难点在于如何确定多边形的形状和顶点,目前已经存在一些解决方案,包括基于 alpha 型和 beta 型形态学的方法、基于分割重组的方法、基于斯蒂尔-沃森(Stiel-Watson)统计的方法等等。

步骤
先求出每个点的单位向量,根据单位向量在极坐标系下的极角从小到大排序,然后按照排序后的顺序依次加入点,如果当前点不是凸包上的点,则一直弹掉栈顶元素直到满足要求,最后栈内存储的即为凸包上所有的点。


代码

#include<iostream>
#include<algorithm>
#include<cmath> 

using namespace std;

struct node {
    long long x, y; // 存储平面内的点的坐标
    int xiang; // 存储所在象限
};

int n;

bool cmp(node x,node y)
{
    // 判断两个点所在象限是否相同
    if(x.xiang != y.xiang)
        return x.xiang < y.xiang;
    // 如果所在象限相同,则判断它们组成的向量与 x 轴之间的夹角大小来排序,
    // 如果向量之间的叉积为正数,表明顺时针旋转需要交换两个向量的位置,这时
    // 我们认为前一个向量更小;如果叉积为负数,表明逆时针旋转不需要交换,这时
    // 认为后一个向量更小。
    return x.x * y.y - x.y * y.x < 0; 
}

// 判断点所在象限的函数
int judge(node x)
{
    if(x.x > 0 && x.y > 0) return 1;
    if(x.x < 0 && x.y > 0) return 2;
    if(x.x < 0 && x.y < 0) return 3;
    if(x.x > 0 && x.y < 0) return 4;
 }

int main()
{
    cin >> n;
    
    struct node a[5010], b[5010];
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i].x >> a[i].y;
    }
    
    double s = -1;
    int cnt;

    for(int i = 1; i <= n; i++)
    {
        cnt = 1;
        for(int j = 1; j <= n; j++)
        {
            if(i == j) continue;
            b[cnt].x = a[j].x - a[i].x;
            b[cnt].y = a[j].y - a[i].y;
            b[cnt].xiang = judge(b[cnt]);
            cnt++;
        } 

        sort(b + 1, b + n, cmp); // 排序

        for(int j = 1; j < n - 1; j++)
        {
            // 计算三角形面积,求最小值
            if(s == -1 || fabs(b[j + 1].x * b[j].y - b[j + 1].y * b[j].x) * 0.5 < s)
                s = fabs(b[j + 1].x * b[j].y - b[j + 1].y * b[j].x) * 0.5;
        }
    }

    printf("%.3f\n", s);
    return 0;
} 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋说

感谢打赏,祝你平安喜乐。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值