HDU1348 Wall 【简单凸包】


【题目大意】

首先读入N,L分别表示有N座城堡以及城墙到城堡的最近距离不得低于L

然后N行输入每个城堡的平面座标

需要求出最短的能够满足条件的城墙长度。

条件:

1)城墙到城堡的最近距离不得低于L

2)城墙必须包含最外圈的所有城堡

输入包含多组数据

保留0位小数输出

【解题思路】

该题是典型的凸包问题

在此简单介绍一下凸包:

凸包(Convex Hull)是一个计算几何(图形学)中的概念。
在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包
X的凸包可以用X内所有点(X1,...Xn)的线性组合来构造.
在二维欧几里得空间中,凸包可想象为一条刚好包著所有点的橡皮圈。
用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边型,它能包含点集中所有的点。
笔者求解凸包常用Andrew算法(Granham算法的变种),稳定性较好
【Andrew算法】
int ConvexHull(Point *P,int n,Point *Q){//返回凸包顶点个数,P为读入的点集,n为点集数量,Q为凸包数组
    sort(P,P+n);
    int m=0;
    for (int i=0;i<n;++i){
    	while (m>1&&(Cross(Q[m-1]-Q[m-2],P[i]-Q[m-2])<=0)) m--;
    	Q[m++]=P[i];
	}
	int k=m;
	for (int i=n-2;i>=0;--i){
		while (m>k&&(Cross(Q[m-1]-Q[m-2],P[i]-Q[m-2])<=0)) m--;
		Q[m++]=P[i];
	}
	if (n>1) m--;
	return m;
}
double Cross(Vector A,Vector B){//叉积
	return A.x*B.y-A.y*B.x;
}


了解了凸包之后,本题就可以快速解决了。
不用理会L,只需要在最后加上2*PI*L(这是因为所有城堡外围所对应的圆心角之和为凸包对应多边形的外角和,即2PI弧度)

【代码】
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cctype>
#include<iomanip>
#include<vector>
#define LL long long
#define UI unsigned int
//#define LOCAL
using namespace std;

struct Point{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;
const double eps=1e-10;
const double PI=acos(-1.0);

Vector operator + (Vector A,Vector B){
	return Vector(A.x+B.x,A.y+B.y);
}

Vector operator - (Point A,Point B){
	return Vector(A.x-B.x,A.y-B.y);
}

Vector operator * (Vector A,double p){
	return Vector(A.x*p,A.y*p);
}

Vector operator / (Vector A,double p){
	return Vector(A.x/p,A.y/p);
}

bool operator < (const Point &A,const Point &B){
	return A.x<B.x || (A.x==B.x&&A.y<B.y);
}

int Vector_Compare(double x){
	return (x<eps) ? 0 : (x<0 ? -1 : 1);
}

bool operator == (const Point &A,const Point &B){
	return Vector_Compare(A.x-B.x)==0&&Vector_Compare(A.y-B.y);
}

double Dot(Vector A,Vector B){
	return A.x*B.x+A.y*B.y;
}

double Cross(Vector A,Vector B){
	return A.x*B.y-A.y*B.x;
}

double Length(Vector A){
	return sqrt(Dot(A,A));
}

double PolgonRound(Point *Q,int n){
	double round=0;
	for (int i=0;i<n-1;++i) round+=Length(Q[i+1]-Q[i]);
	round+=Length(Q[0]-Q[n-1]);
	return round;
}

int ConvexHull(Point *P,int n,Point *Q){
    sort(P,P+n);
    int m=0;
    for (int i=0;i<n;++i){
    	while (m>1&&(Cross(Q[m-1]-Q[m-2],P[i]-Q[m-2])<=0)) m--;
    	Q[m++]=P[i];
	}
	int k=m;
	for (int i=n-2;i>=0;--i){
		while (m>k&&(Cross(Q[m-1]-Q[m-2],P[i]-Q[m-2])<=0)) m--;
		Q[m++]=P[i];
	}
	if (n>1) m--;
	return m;
}

const int N=1011;
int T;
int n,L;
Point P[N],Q[N];

int main(){
#ifdef LOCAL
    freopen("HDU1348.in","r",stdin);
#endif 
    scanf("%d",&T);
    while (T--){
    	double ans=0;
    	int opt=0;
    	scanf("%d%d",&n,&L);
    	for (int i=0;i<n;++i){
    		scanf("%lf%lf",&P[i].x,&P[i].y);
		}
		int num=ConvexHull(P,n,Q);
		ans=PolgonRound(Q,num);
		ans+=2*PI*L;
		if (T!=0) printf("%.0lf\n\n",ans);//注意本题的格式,有坑
		else printf("%.0lf\n",ans);
	}
	return 0;
}



【总结】
凸包求法以及多边形外角和的巧妙处理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章