求凸包

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;
}

posted on 2012-08-21 16:39  more think, more gains  阅读(276)  评论(0编辑  收藏  举报

导航