Surround the Trees
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.
Zero at line for number of trees terminates the input for your program.
原题链接: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;
}