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

转载于:https://www.cnblogs.com/WArobot/p/6822125.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值