HDU 3263 Ancient vending machine(多边形内直径和最小宽度)

该博客讨论了如何判断一个硬币能否穿过一个多边形孔,涉及计算几何问题。通过求多边形的凸包来确定硬币的最小宽度,以及在凹多边形孔中找到最大内直径的方法。具体解决方案包括对硬币求凸包并枚举边,以及按重心排序多边形孔的顶点以排除凹陷部分。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:
HDU 3263 Ancient vending machine
题意:
给出一个多边形孔和一个多边形硬币,问你这个硬币能能否从孔中穿过去?
分析:
对于多边形孔是求最大内直径H,多边形硬币是求在某个方向上的最小宽度L。
先来看L.先对多边形硬币求个凸包,枚举这个凸包的每一条边,求出所有顶点距离这条边的距离的最大值,这就是这条边的多对应的高度,在所有边的高度中取最小值就是最小宽度L.
再来看H.首先H一定是在多边形的某两个顶点处产生,如果多边形孔是一个凸包的话,那很好办,直接暴力扫一遍顶点间距离然后取最大即可。但是孔可能是凹多边形啊。先把孔的所有顶点以重心为基准顺时针排序。如果在顶点i和顶点j之间有一个顶点k是凹进去的,那么重心到k的距离一定小于重心到线段i–j的距离,利用这个判断就可去掉不合法的顶点距离了。在所有合法距离中取最大值就是H。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <climits>
#include <cmath>
#include <ctime>
#include <cassert>
#include <iomanip>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
const double eps = 1e-10;
const double pi = acos(-1.0);
const int MAX_N = 50;

int T, n, m;

struct Point{
    double x, y;

    Point() {}
    Point(double _x, double _y) :x(_x), y(_y) {}
    Point operator + (const Point& rhs) const {
        return Point(x + rhs.x, y + rhs.y);
    }
    Point operator - (const Point& rhs) const {
        return Point(x - rhs.x, y - rhs.y);
    }
    Point operator * (const double d ) const {
        return Point(x * d, y * d);
    }
    double dis(const Point& rhs) const {
        return hypot(x - rhs.x, y - rhs.y);
    }
    double dot(const Point& rhs) const {
        return (x * rhs.x + y * rhs.y);
    }
    double cross(const Point& rhs) const {
        return (x * rhs.y - y * rhs.x);
    }
}point1[MAX_N], point2[MAX_N], vextex1[MAX_N], vertex2[MAX_N], center;

inline bool cmp_x(const Point a, const Point b)
{
    if(a.x == b.x) return a.y < b.y;
    return a.x < b.x;
}
//求凸包
inline int Andrew()
{
    sort(point2,point2 + m, cmp_x);
    int k = 0;
    for(int i = 0; i < m; i++){
        while(k > 1 && (vertex2[k - 1] - vertex2[k - 2]). cross(point2[i] - vertex2[k - 1]) <= 0){
            k --;
        }
        vertex2[k++] = point2[i];
    }
    int m = k;
    for(int i = n - 2; i >= 0; i--){
        while(k > m && (vertex2[k -1] - vertex2[k - 2]).cross(point2[i] - vertex2[k - 1]) <= 0){
            k --;
        }
        vertex2[k++] = point2[i];
    }
    if(k > 1) k--;
    return k;
}

//计算c到a--b的距离
inline double GetHight(Point a, Point b, Point c)
{
    double d1 = a.dis(b);
    double d2 = (b - a).cross(c - a);
    return fabs(d2 / d1);
}

//计算凸包边相对顶点的最远距离,在所有距离中取最小值
inline double GetCoin(int k)
{
    vertex2[k] = vertex2[0];
    double res = 1e6;
    for(int i = 0; i < k; i++){
        double tmp = -1.0;
        for(int j = 0; j < k; j++){
            tmp = max(tmp, GetHight(vertex2[i], vertex2[i + 1], vertex2[j]));
        }
        res = min(res, tmp);
    }
    return res;
}

//判断点q是否在线段p1--p2上
inline bool on_seg(Point p1, Point p2, Point q)
{
    return (p1 - q).cross(p2 - q) == 0 && (p1 - q).dot(p2 - q) <= 0;
}

inline bool cmp(const Point a, const Point b)
{
    double res = (a - center).cross(b - center);
    if(res < eps) return true;
    if(res > eps) return false;
    double d1 = a.dis(center);
    double d2 = b.dis(center);
    return d1 > d2;
}

//将多边形1的所有顶点以重心为中心顺时针排序
inline void ClockWise()
{
    double  x = 0, y = 0;
    for(int i = 0; i< n; i++){
        x += point1[i].x;
        y += point1[i].y;
    }
    //center为重心
    center.x = x / n, center.y = y / n;
    sort(point1, point1 + n, cmp);
    point1[n].x = point1[0].x ,point1[n].y = point1[0].y;
}

//判断多边形1的两个顶点i和j连线合法
inline bool check(int i, int j)
{//也就是判断是否在i和j之间是否有顶点在线段i--j和重心之间
    double hight = GetHight(point1[i], point1[j], center); //重心到线段i--j的距离
    for(int k = i + 1; k < j; k++){
        double d = point1[k].dis(center); //d是顶点k到重心的距离
        if( d < hight) return false;
    }
    return true;
}

//获取多边形1内合法的顶点间距离的最大距离
inline double GetDiameter()
{
    double res = -1.0;
    for(int i = 0; i < n; i++){
        for(int j = i + 1; j < n; j++){
            if(check(i, j)) res = max(res, point1[i].dis(point1[j]));
        }
    }
    return res;
}

int main()
{
    IOS;
    cin >> T;
    while(T--){
        cin >> n;
        for(int i = 0; i < n; i++){
            cin >> point1[i].x >> point1[i].y;
        }
        cin >> m;
        for(int i = 0; i < m; i++){
            cin >> point2[i].x >> point2[i].y;
        }
        double L = GetCoin(Andrew());
        ClockWise();
        double H = GetDiameter();
        if(H + eps >= L) cout << "legal" << endl;
        else cout << "illegal" << endl;

    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值