HDU1392 Surround the Trees(凸包模版题)

HDU1392 Surround the Trees 

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 11173    Accepted Submission(s): 4337


Problem Description

There are a lot of trees in an area. A peasant wants to buy a rope to surround all these trees. So at first he must know the minimal required length of the rope. However, he does not know how to calculate it. Can you help him?     
The diameter and length of the trees are omitted, which means a tree can be seen as a point. The thickness of the rope is also omitted which means a rope can be seen as a line.


There are no more than 100 trees.

Input

The input contains one or more data sets. At first line of each input data set is number of trees in this data set, it is followed by series of coordinates of the trees. Each coordinate is a positive integer pair, and each integer is less than 32767. Each pair is separated by blank.

Zero at line for number of trees terminates the input for your program.

Output

The minimal length of the rope. The precision should be 10^-2.

Sample Input

9

12 7

24 9

30 5

41 9

80 7

50 87

22 9

45 1

50 7

0




Sample Ouput

243.06


题目大意

给你一个数n,代表一共有多少课树,然后给你n对点表示每棵树的坐标,要你求一个多边形能把所有的点包起来。
典型的凸包,Graham算法题,直接套模板就可以了。
  要注意的是当树只有一棵或者两棵的情况,因为不会形成凸包,所以需要单独处理一下。

这个是一般计算几何里要用到的关于点和线的处理的模板,比较全


以下是关于点的
const double eps = 1e-8;
const double PI = acos(-1.0);
int sgn(double x)
{
	if(fabs(x) < eps)return 0;
    if(x < 0)return -1;
    else return 1;
}
struct Point
{
    double x,y;
    Point(){}
    Point(double _x,double _y)
    {
        x = _x;y = _y;
    }
    Point operator -(const Point &b)const
    {
        return Point(x - b.x,y - b.y);
    }
    //叉积
    double operator ^(const Point &b)const
    {
        return x*b.y - y*b.x;
    }
    //点积
    double operator *(const Point &b)const
    {
        return x*b.x + y*b.y;
    }
    //绕原点旋转角度B(弧度值),后x,y的变化
    void transXY(double B)
    {
        double tx = x,ty = y;
        x = tx*cos(B) - ty*sin(B);
        y = tx*sin(B) + ty*cos(B);
    }
};
关于线的
struct Line
{
    Point s,e;
    Line(){}
    Line(Point _s,Point _e)
    {
        s = _s;e = _e;
    }
//两直线相交求交点
//第一个值为0表示直线重合,为1表示平行,为0表示相交,为2是相交
//只有第一个值为2时,交点才有意义
    pair<int,Point> operator &(const Line &b)const
    {
        Point res = s;
    if(sgn((s-e)^(b.s-b.e)) == 0)
    {
        if(sgn((s-b.e)^(b.s-b.e)) == 0)
            return make_pair(0,res);//重合
        else return make_pair(1,res);//平行
    }
    double t = ((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
    res.x += (e.x-s.x)*t;
    res.y += (e.y-s.y)*t;
    return make_pair(2,res);
    }
};
求两点距离的
double dist(Point a,Point b)
{
    return sqrt((a-b)*(a-b));
}
接下来这个就是 Graham算法题的模版
点的编号0~n-1,存在list[MAXN]里
返回凸包结果Stack[0~top-1]
Stack[]里就是按顺序存的凸包的点在list[]的序号
void Graham(int n)
{
    Point p0;
    int k = 0;
    p0 = list[0];
    //找最下边的一个点
    for(int i = 1;i < n;i++)
    {
        if( (p0.y > list[i].y) || (p0.y == list[i].y && p0.x > list[i].x) )
        {
            p0 = list[i];
            k = i;
        }
    }
    swap(list[k],list[0]);
    sort(list+1,list+n,_cmp);
    if(n == 1)
    {
        top = 1;
        Stack[0] = 0;
        return;
    }
    if(n == 2)
    {
        top = 2;
        Stack[0] = 0;
        Stack[1] = 1;
        return ;
    }
    Stack[0] = 0;
    Stack[1] = 1;
    top = 2;
    for(int i = 2;i < n;i++)
    {
        while(top > 1 &&sgn((list[Stack[top-1]]-list[Stack[top-2]])^(list[i]-list[Stack[top-2]])) <=0)
        top--;
        Stack[top++] = i;
    }
}

以下是全部代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const double eps = 1e-8;
const double PI = acos(-1.0);
int sgn(double x)
{
	if(fabs(x) < eps)return 0;
    if(x < 0)return -1;
    else return 1;
}
struct Point
{
    double x,y;
    Point(){}
    Point(double _x,double _y)
    {
        x = _x;y = _y;
    }
    Point operator -(const Point &b)const
    {
        return Point(x - b.x,y - b.y);
    }
    //叉积
    double operator ^(const Point &b)const
    {
        return x*b.y - y*b.x;
    }
    //点积
    double operator *(const Point &b)const
    {
        return x*b.x + y*b.y;
    }
    //绕原点旋转角度B(弧度值),后x,y的变化
    void transXY(double B)
    {
        double tx = x,ty = y;
        x = tx*cos(B) - ty*sin(B);
        y = tx*sin(B) + ty*cos(B);
    }
};
struct Line
{
    Point s,e;
    Line(){}
    Line(Point _s,Point _e)
    {
        s = _s;e = _e;
    }
//两直线相交求交点
//第一个值为0表示直线重合,为1表示平行,为0表示相交,为2是相交
//只有第一个值为2时,交点才有意义
    pair<int,Point> operator &(const Line &b)const
    {
        Point res = s;
    if(sgn((s-e)^(b.s-b.e)) == 0)
    {
        if(sgn((s-b.e)^(b.s-b.e)) == 0)
            return make_pair(0,res);//重合
        else return make_pair(1,res);//平行
    }
    double t = ((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
    res.x += (e.x-s.x)*t;
    res.y += (e.y-s.y)*t;
    return make_pair(2,res);
    }
};
double dist(Point a,Point b)
{
    return sqrt((a-b)*(a-b));
}
const int MAXN = 1010;
Point list[MAXN];
int Stack[MAXN],top;
//相对于list[0]的极角排序
bool _cmp(Point p1,Point p2)
{
    double tmp = (p1-list[0])^(p2-list[0]);
    if(sgn(tmp) > 0)return true;
    else if(sgn(tmp) == 0 && sgn(dist(p1,list[0]) - dist(p2,list[0])) <= 0)
    return true;
    else return false;
}
void Graham(int n)
{
    Point p0;
    int k = 0;
    p0 = list[0];
    //找最下边的一个点
    for(int i = 1;i < n;i++)
    {
        if( (p0.y > list[i].y) || (p0.y == list[i].y && p0.x > list[i].x) )
        {
            p0 = list[i];
            k = i;
        }
    }
    swap(list[k],list[0]);
    sort(list+1,list+n,_cmp);
    if(n == 1)
    {
        top = 1;
        Stack[0] = 0;
        return;
    }
    if(n == 2)
    {
        top = 2;
        Stack[0] = 0;
        Stack[1] = 1;
        return ;
    }
    Stack[0] = 0;
    Stack[1] = 1;
    top = 2;
    for(int i = 2;i < n;i++)
    {
        while(top > 1 &&sgn((list[Stack[top-1]]-list[Stack[top-2]])^(list[i]-list[Stack[top-2]])) <=0)
        top--;
        Stack[top++] = i;
    }
}
int main(){
	int n;
	while(scanf("%d",&n)&&n){
		for(int i=0;i<n;i++){
			scanf("%lf%lf",&list[i].x,&list[i].y);
		}
		double ans;
		//当树只有一棵时,就为0 
		if(n==1){
			ans=0.0; 
			printf("%.2lf\n",ans);
			continue;
		}
		//当树只有两棵时,不会形成凸包,直接求两点距离就好了 
		if(n==2){
			ans=dist(list[0],list[1]);
			printf("%.2lf\n",ans);
			continue;
		}
		Graham(n);//这就是套用模版直接求出凸包
		//求出凸包后,求凸包的周长 
		ans=dist(list[Stack[top-1]],list[Stack[0]]);
		for(int i=0;i<top-1;i++){
			ans+=dist(list[Stack[i]],list[Stack[i+1]]);
		}
		
		printf("%.2lf\n",ans);
	}
	return 0;
}


模版里有很多子函数其实并没有用到,但是函数很全,一般做计算几何的题目,用这些模板足够了。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值