求凸包

本文解决了一个有趣的问题:如何用最短的围墙包围乔治的所有狗狗喜欢的放牧地点。通过计算几何和凸包算法,我们找到了一种有效的方法来确定所需的最短围墙长度。

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

C-Dogs' House

Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)
Total Submission(s) : 134   Accepted Submission(s) : 45
Font: Times New Roman | Verdana | Georgia
Font Size: ← →

Problem Description

George wishes to build a House to contain his dogs(without roof), but he's a bit short on cash right. Any Wall he builds must contain all of the favorite grazing spots for his dogs. Given the location of these spots, determine the length of the shortest Wall which encloses them.

Input

There are several test cases.
In each test case:
The first line of the input file contains one integer, N. N (0 <= N <= 10,000) is the number of grazing spots that George wishes to enclose. The next N line consists of two real numbers, Xi and Yi, corresp
onding to the location of the grazing spots in the plane (-1,000,000 <= Xi,Yi <= 1,000,000). The numbers will be in decimal format.

Output

The output should consist of one real number, the length of fence required. The output should be accurate to two decimal places.

Sample Input

4
4 8
4 12
5 9.3
7 8

Sample Output

12.00
View Code
#include <algorithm>
#include <iostream>
#include <vector>
#include <math.h>
using namespace std;
//二维点(或向量)结构体定义
#ifndef _WINDEF_
struct POINT { double x; double y; };
#endif
typedef vector<POINT> PTARRAY;
//判断两个点(或向量)是否相等
bool operator==(const POINT &pt1, const POINT &pt2) {
    return (pt1.x == pt2.x && pt1.y == pt2.y);
}
// 比较向量中哪个与x轴向量(1, 0)的夹角更大
bool CompareVector(const POINT &pt1, const POINT &pt2) {
    //求向量的模
    float m1 = sqrt((float)(pt1.x * pt1.x + pt1.y * pt1.y));
    float m2 = sqrt((float)(pt2.x * pt2.x + pt2.y * pt2.y));
    //两个向量分别与(1, 0)求内积
    float v1 = pt1.x / m1, v2 = pt2.x / m2;
    //如果向量夹角相等,则返回离基点较近的一个,保证有序
    return (v1 > v2 || v1 == v2 && m1 < m2);
}
//计算凸包
void CalcConvexHull(PTARRAY &vecSrc) {
    //点集中至少应有3个点,才能构成多边形
    if (vecSrc.size() < 3) {
        return;
    }
    //查找基点
    POINT ptBase = vecSrc.front(); //将第1个点预设为最小点
    for (PTARRAY::iterator i = vecSrc.begin() + 1; i != vecSrc.end(); ++i) {
        //如果当前点的y值小于最小点,或y值相等,x值较小
        if (i->y < ptBase.y || (i->y == ptBase.y && i->x > ptBase.x)) {
            //将当前点作为最小点
            ptBase = *i;
        }
    }
    //计算出各点与基点构成的向量
    for (PTARRAY::iterator i = vecSrc.begin(); i != vecSrc.end();) {
        //排除与基点相同的点,避免后面的排序计算中出现除0错误
        if (*i == ptBase) {
            i = vecSrc.erase(i);
        }
        else {
            //方向由基点到目标点
            i->x -= ptBase.x, i->y -= ptBase.y;
            ++i;
        }
    }
    //按各向量与横坐标之间的夹角排序
    sort(vecSrc.begin(), vecSrc.end(), &CompareVector);
    //删除相同的向量
    vecSrc.erase(unique(vecSrc.begin(), vecSrc.end()), vecSrc.end());
    //计算得到首尾依次相联的向量
    for (PTARRAY::reverse_iterator ri = vecSrc.rbegin();
        ri != vecSrc.rend() - 1; ++ri) {
        PTARRAY::reverse_iterator riNext = ri + 1;
        //向量三角形计算公式
        ri->x -= riNext->x, ri->y -= riNext->y;
    }
    //依次删除不在凸包上的向量
    for (PTARRAY::iterator i = vecSrc.begin() + 1; i != vecSrc.end(); ++i) {
        //回溯删除旋转方向相反的向量,使用外积判断旋转方向
        for (PTARRAY::iterator iLast = i - 1; iLast != vecSrc.begin();) {
            double v1 = i->x * iLast->y, v2 = i->y * iLast->x;
            //如果叉积小于0,则无没有逆向旋转
            //如果叉积等于0,还需判断方向是否相逆
            if (v1 < v2 || (v1 == v2 && i->x * iLast->x > 0 &&
                i->y * iLast->y > 0)) {
                    break;
            }
            //删除前一个向量后,需更新当前向量,与前面的向量首尾相连
            //向量三角形计算公式
            i->x += iLast->x, i->y += iLast->y;
            iLast = (i = vecSrc.erase(iLast)) - 1;
        }
    }
    //将所有首尾相连的向量依次累加,换算成坐标
    vecSrc.front().x += ptBase.x, vecSrc.front().y += ptBase.y;
    for (PTARRAY::iterator i = vecSrc.begin() + 1; i != vecSrc.end(); ++i) {
        i->x += (i - 1)->x, i->y += (i - 1)->y;
    }
    //添加基点,全部的凸包计算完成
    vecSrc.push_back(ptBase);
}
 
int main(void) {
    int nPtCnt; //生成的随机点数
    while( scanf("%d", &nPtCnt) != EOF )
    {
     PTARRAY vecSrc, vecCH;
     for (int i = 0; i < nPtCnt; ++i) {
        POINT ptIn;
        scanf("%lf%lf",&ptIn.x,&ptIn.y);
        vecSrc.push_back(ptIn);
    }
    CalcConvexHull(vecSrc);
    //cout << "\nConvex Hull:\n";
    PTARRAY::iterator ixx = vecSrc.begin(), i, ix;
    ix = ixx;
    i = ++ixx;
    ixx--;
    double sum = 0;
    for ( ; i != vecSrc.end(); ++i) {
      //  cout << i->x << ", " << i->y << endl;
      sum += sqrt( (i->x - ix->x) * (i->x - ix->x) +  (i->y - ix->y) *  (i->y - ix->y) );
      ix = i;
    }
    i = ix;
    ix = ixx;
    sum += sqrt( (i->x - ix->x) * (i->x - ix->x) +  (i->y - ix->y) *  (i->y - ix->y) );
    printf("%.2lf\n",sum);
    }
    return 0;
}

转载于:https://www.cnblogs.com/tangcong/archive/2012/08/21/2649340.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值