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



【總結】
凸包求法以及多邊形外角和的巧妙處理

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