HDU 1397——Surround the Trees(凸包问题,graham扫描法)

Surround the Trees

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 Output
243.06

原题链接:https://acm.hdu.edu.cn/showproblem.php?pid=1392

题意:有很多点表示的树的位置,要用一根绳子围起来,问最小的长度。

思路:很明显的凸包问题,找到构成的凸包,算距离就可以,一道模板题。

最近看了一下graham扫描法,这个方法确实很好用,尝试着自己写了一下,能过。

这里浅浅的说一下graham扫描法:

1、首先对所给的点找到最左下的点,然后以这个点为原点建立直角坐标系,分别计算其余点与原点的距离,以及与原点形成的夹角。

2、然后对角度排序,角度小的在前面,角度相同,距离小的在前面。

3、排好序后的第一个点和第二个点一定是凸包上的点,先将两个点入栈。然后遍历排好序的点,每个点c(xc,yc)与栈顶的第一个点a(xa,ya),第二个点b(xb,yb)判断关系(这里用到叉积判断);因为是逆时针找,所以我们找的凸包肯定是第一中情况。判断ac与ab的叉积(右手螺旋相信大家都很熟悉),当叉积大于0时,这个点c是凸包上的点,否则不是,弹出栈顶的点重复进行操作,直到找到满足当前点叉积大于0的情况把c入栈,因为z=0,所以此时的叉积为(xc-xa)*(yb-ya)-(xb-xa)*(yc-ya)。

讲的可能不是很详细,推荐一篇我看的博客。

https://blog.csdn.net/u012328159/article/details/50808360

 AC代码:  

(这个代码的角度这里我用的sin表示,因为sin从[-Π/2,Π/2]是单调的,所以也可以表示角度的大小。)

#include<iostream>
#include<algorithm>
#include<stdio.h> 
#include<cmath>
using namespace std;
const int maxn=1e5+10;
struct Node{              //结构体用来存点的信息 
	int x,y;
	double dis,sin_x;
}node[maxn],ans[maxn];   //node存给的点,ans存最终构成凸包的点 
int cnt=0;
int cmp(Node a,Node b){          //按照每个点的坐标排序 
	if(a.x==b.x) return a.y<b.y;
	return a.x<b.x;
}
void dis(int n){       //计算算每个点与原点的距离及夹角 
	for(int i=1;i<n;i++){
		node[i].dis=(double)sqrt(pow(node[i].x-node[0].x,2)+pow(node[i].y-node[0].y,2));
		node[i].sin_x=(double)(node[i].y-node[0].y)/node[i].dis;
	}
}
int cmp1(Node a,Node b){       //对角度和到原点的距离排序 
	if(a.sin_x==b.sin_x) return a.dis<b.dis;
	return a.sin_x<b.sin_x;
}
bool judge(int ni){            //判断这个点是不是凸包上的点 
	if(cnt<2) return true;
	Node a=ans[cnt-1];
	Node b=ans[cnt-2];
	Node c=node[ni];
	if((c.x-a.x)*(b.y-a.y)-(b.x-a.x)*(c.y-a.y)<=0) return false;
	else return true;
}
void lang(){
	double sum=0;
	for(int i=1;i<cnt;i++){      //计算距离 
		sum+=(double)sqrt(pow(ans[i].x-ans[i-1].x,2)+pow(ans[i].y-ans[i-1].y,2));
	}
	//如果只有两个点,则不需要计算最后一个点和第0个点的距离 
	if(cnt>2) sum+=(double)sqrt(pow(ans[0].x-ans[cnt-1].x,2)+pow(ans[0].y-ans[cnt-1].y,2));
	printf("%.2lf\n",sum);
}
int main(){
	int n;
	while(scanf("%d",&n)){
		if(n==0) break;
		cnt=0;
		for(int i=0;i<n;i++) scanf("%d%d",&node[i].x,&node[i].y);
		if(n==1){            //如果只有一个点,不需要绳子围住 
			printf("0.00\n");
			continue;
		}
		sort(node,node+n,cmp);     //对点排序,找到最下边的那个点 
		
		node[0].dis=0;
		node[0].sin_x=-1;
		dis(n);
		sort(node,node+n,cmp1);
		
		ans[cnt++]=node[0];      //前两个点一定是凸包上的点 
		ans[cnt++]=node[1];
		for(int i=2;i<n;i++){
			while(!judge(i)) cnt--;     //找这个点在凸包的位置 
			ans[cnt++]=node[i];
		}
		lang();    //计算距离 
	}	
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

烊@

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值