HDU 1348 Wall ( 凸包周长 )


**链接:****传送门 **

题意:给出二维坐标轴上 n 个点,这 n 个点构成了一个城堡,国王想建一堵墙,城墙与城堡之间的距离总不小于一个数 L ,求城墙的最小长度,答案四舍五入

思路:城墙与城堡直线长度是相等的,当城堡出现拐角时,城墙必然会出现一段圆弧,这些圆弧最终会构成一个半径为 L 的圆,所以答案就是凸包的周长 + 圆的周长

balabala:

  1. 采用Jarvis步进法来求凸包,Jarvis步进法复杂度为O(nh),h为凸包顶点个数
  2. 采用Graham-Scan来求凸包,Graham - Scan 法复杂度为O(nlogn)

如有错误请一定指出!


Graham - Scan法:

/*************************************************************************
    > File Name: hdu1348t2.cpp
    > Author:    WArobot 
    > Blog:      http://www.cnblogs.com/WArobot/ 
    > Created Time: 2017年05月07日 星期日 20时49分15秒
 ************************************************************************/

#include<bits/stdc++.h>
using namespace std;

// Graham - Scan 
// O(nlgn)

#define PI 3.1415926535
const int maxn = 1010;
struct point{
	double x,y;
};


bool cmp(point a,point b){
	return (a.y<b.y || (a.y==b.y && a.x<b.x));
}
bool mult(point sp,point ep,point op){
	return (sp.x-op.x)*(ep.y-op.y)>=(sp.y-op.y)*(ep.x-op.x);
}
point res[maxn];
int Graham(point pnt[],int n){
	int i , len , k = 0 , top = 1;
	sort(pnt,pnt+n,cmp);
	if(n == 0)	return 0;	res[0] = pnt[0];
	if(n == 1)	return 1;	res[1] = pnt[1];
	if(n == 2)	return 2;	res[2] = pnt[2];
	for(int i=2;i<n;i++){
		while( top && mult( pnt[i] , res[top] , res[top-1] ))
			top--;
		res[++top] = pnt[i];
	}
	len = top;	res[++top] = pnt[n-2];
	for(i=n-3;i>=0;i--){
		while( top!=len && mult( pnt[i] , res[top] , res[top-1] ))
			top--;
		res[++top] = pnt[i];
	}
	return top;
}
double point_dis(point a,point b){
	return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
int main(){
	int T , n , L , kase = 0;
	point pi[maxn];
	scanf("%d",&T);
	while(T--){
		if(kase > 0)	printf("\n");
		kase++;
		scanf("%d%d",&n,&L);
		for(int i=0;i<n;i++)	scanf("%lf%lf",&pi[i].x,&pi[i].y);
		int t = Graham(pi,n);
		double ans = 2*PI*L;
		for(int i=0;i<t;i++){
			ans += point_dis( res[i] , res[ (i+1)%t ] );
		}
		printf("%.lf\n",ans);
	}
	return 0;
}

Jarvis步进法:

/*************************************************************************
    > File Name: hdu1348.cpp
    > Author:    WArobot 
    > Blog:      http://www.cnblogs.com/WArobot/ 
    > Created Time: 2017年05月07日 星期日 18时55分57秒
 ************************************************************************/

#include<bits/stdc++.h>
using namespace std;

#define PI 3.1415926535
const int maxn = 1010;
struct point{
	double x,y;
}pi[maxn];

bool cmp(point a,point b){
	return ( a.y<b.y || a.y==b.y && a.x<b.x);
}

int n,L,ans[maxn],cnt,sta[maxn],tail;
// 检查是否严格左转,共线不算左转
bool CrossLeft(point p1,point p2,point p3){	
	return ((p3.x-p1.x)*(p2.y-p1.y) - (p2.x-p1.x)*(p3.y-p1.y)) < 0;
}
void Jarvis(){
	tail = cnt = 0;	
	sort(pi,pi+n,cmp);
	sta[tail++] = 0;	sta[tail++] = 1;
	for(int i=2;i<n;i++){
		while(tail>1 && !CrossLeft( pi[ sta[tail-1] ] , pi[ sta[tail-2] ] , pi[i] ))
			tail--;
		sta[ tail++ ] = i;
	}
	for(int i=0;i<tail;i++)	ans[cnt++] = sta[i];
	tail = 0;	sta[ tail++ ] = n-1;	sta[ tail++ ] = n-2;
	for(int i=n-3;i>=0;i--){
		while(tail>1 && !CrossLeft( pi[ sta[tail-1] ] , pi[ sta[tail-2] ] , pi[i] ))
			tail--;
		sta[ tail++ ] = i;
	}
	for(int i=0;i<tail;i++)	ans[cnt++] = sta[i];
}
double Point_dis(point a,point b){
	return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
int main(){
	int T , kase = 0;
	scanf("%d",&T);
	while(T--){
		if(kase)	printf("\n");
		kase++;
		scanf("%d%d",&n,&L);
		for(int i=0;i<n;i++)	scanf("%lf%lf",&pi[i].x,&pi[i].y);
		Jarvis();
		double re = 2*PI*L;
		for(int i=0;i<cnt-1;i++){
			re += Point_dis( pi[ans[i]] , pi[ans[i+1]] );
		}
		printf("%.0lf\n",re);
	}
	return 0; 
}
posted @ 2017-05-07 20:27  ojnQ  阅读(341)  评论(0编辑  收藏  举报