POJ 1113 wall 求凸包

题意

求把给出的点围起来一面墙且距离最小为L

分析

根据下图可以知道其实就是求凸包加上一个半径为L的圆周长

那么就是一个裸的凸包问题啦

一般来说求凸包有两种做法,卷包裹法和 Graham-Scan


看起来卷包裹更优秀呢(对于这题



       卷包裹的原理比较简单:先找一个最边缘的点(一般是最左下角的),假设有一条绳子,以该点为端点向右边逆时针旋转直到碰到另一个点为止,此时找出凸包的一条边;然后再用新的点作为端点,继续旋转绳子,找到下一个端点;重复这一步骤直至围成一个凸多边形,即可得到这个点集的凸包。时间复杂度为O(n^2)

步骤:

i)选择点集左下角的点,该点为凸包的第一个点

ii)以水平向右的方向做射线,逆时针旋转,选择第一条在初始射线之上的射线作为当前射线,当前射线经过凸包的第二个点。

iii)以当前射线为基准,继续逆时针旋转至最靠近该射线的一条射线,找到凸包的下一个点。把这条射线作为基准重复上述操作,至到回到起始点。

通过当前基准射线寻找下一个射线有以下几种方式:

i)把每一条射线与其他n-2条线比较每步效率是o(n^2)

ii)通过计算各射线与当前基准射线的夹角的方式,效率是o(n)但是会存在浮点数误差。

ii)利用叉积运算求各射线斜率的相对关系,从而求得下一条射线,效率o(n)且不存在误差

代码:

#pragma warning(disable:4786)//使命名长度不受限制
#pragma comment(linker, "/STACK:102400000,102400000")//手工开栈
#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <stack>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d,&x,&y,&z)
#define rdl(x) scanf("%I64d,&x);
#define rds(x) scanf("%s",x)
#define rdc(x) scanf("%c",&x)
#define ll long long int
#define ull unsigned long long
#define maxn 1111
#define mod 1000000007
#define INF 0x3f3f3f3f //int 最大值
#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i)
#define MT(x,i) memset(x,i,sizeof(x))
#define PI  acos(-1.0)
#define E  exp(1)
#define eps 1e-8
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll mul(ll a,ll b,ll p){ll sum=0;for(;b;a=(a+a)%p,b>>=1)if(b&1)sum=(sum+a)%p;return sum;}
inline void Scan(int &x) {
      char c;while((c=getchar())<'0' || c>'9');x=c-'0';
      while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
using namespace std;
int sgn(double x){
    if(fabs(x)<eps)return 0;
    if(x<0)return -1;
    return 1;
}
struct Point{
    double x,y;
    Point(){}
    Point(double _x,double _y){
        x=_x;
        y=_y;
    }
    bool operator <(const Point &b)const{
        return sgn(x-b.x)==0?sgn(y-b.y):x<b.x;
    }
    double distance(const Point &p)const{
        return hypot(x-p.x,y-p.y);
    }
}my[maxn];
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;
}
int ans[maxn],sta[maxn],tail,cnt,n,l;
void jarvis(){
    tail=cnt=0;sort(my,my+n);
    sta[tail++]=0;sta[tail++]=1;
    for(int i=2;i<n;++i){
        while(tail>1&&!crossLeft(my[sta[tail-1]],my[sta[tail-2]],my[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(my[sta[tail-1]],my[sta[tail-2]],my[i]))tail--;
        sta[tail++]=i;
    }
    for(int i=0;i<tail;++i)ans[cnt++]=sta[i];
}
int main(){
    double x,y;
    while(~scanf("%d%d",&n,&l)){
        for(int i=0;i<n;++i){
            scanf("%lf%lf",&x,&y);
            my[i]=Point(x,y);
        }
        jarvis();
        double re=4.0*acos(0.0)*l;
        for(int i=0;i<cnt-1;++i)
            re+=my[ans[i]].distance(my[ans[i+1]]);
        printf("%.0f\n",re);
    }
    return 0;
}


Graham-Scan :

利用极坐标,从点集中先找出一个最下方的点,这个点为凸包上的点,将所有的点 根据该点进行极角排序,并同时使用一个栈结构维护凸包上的点。按照极角序依次将点与栈顶的两个点做拐点判断:若右拐则将当前点加入栈中;否则,将栈顶的点弹出。当遍历完点集后,还在栈中的点就是凸包上的点,而且依次出栈可以得到从起点开始顺时针旋转的所有凸包上的点。

code:

#pragma warning(disable:4786)//使命名长度不受限制
#pragma comment(linker, "/STACK:102400000,102400000")//手工开栈
#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <stack>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d,&x,&y,&z)
#define rdl(x) scanf("%I64d,&x);
#define rds(x) scanf("%s",x)
#define rdc(x) scanf("%c",&x)
#define ll long long int
#define ull unsigned long long
#define maxn 1005
#define mod 1000000007
#define INF 0x3f3f3f3f //int 最大值
#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i)
#define MT(x,i) memset(x,i,sizeof(x))
#define PI  acos(-1.0)
#define E  exp(1)
#define eps 1e-8
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll mul(ll a,ll b,ll p){ll sum=0;for(;b;a=(a+a)%p,b>>=1)if(b&1)sum=(sum+a)%p;return sum;}
inline void Scan(int &x) {
      char c;while((c=getchar())<'0' || c>'9');x=c-'0';
      while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
using namespace std;
int sgn(double x){
    if(fabs(x)<0)return 0;
    if(x<0)return -1;
    return 1;
}
struct Point{
    double x,y;
    Point(){}
    Point(double _x,double _y){
        x=_x;
        y=_y;
    }
    double distance(const Point &b)const{
        return hypot(x-b.x,y-b.y);
    }
}my[maxn];
int crossLeft(Point p0,Point p1,Point p2) {
    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
int sta[maxn],top;
bool cmp(Point a,Point b){
    int tmp=crossLeft(my[0],a,b);
    if(tmp>0)return 1;
    if(!tmp&&my[0].distance(a)<my[0].distance(b))return 1;
    return 0;
}
int n,l;
void graham(){
    if(n==1){top=0;sta[0]=0;}
    if(n==2){
        top=1;
        sta[0]=0;
        sta[1]=1;
    }
    if(n>2){
        for(int i=0;i<=1;++i)sta[i]=i;
        top=1;
        for(int i=2;i<n;++i){
            while(top>0&&crossLeft(my[sta[top-1]],my[sta[top]],my[i])<=0)top--;
            sta[++top]=i;
        }
    }
}
int main(){
    while(~scanf("%d%d",&n,&l)){
        Point tmp;
        int k=0;
        for(int i=0;i<n;++i){
            double x,y;
            scanf("%lf%lf",&x,&y);
            my[i]=Point(x,y);
            if(i!=0){
                if((tmp.y>my[i].y)||((tmp.y==my[i].y)&&(tmp.x>my[i].x))){
                    tmp=my[i];
                    k=i;
                }
            }
            else tmp=my[i];
        }
        my[k]=my[0];
        my[0]=tmp;
        sort(my+1,my+n,cmp);
        graham();
        double ans=2*PI*l;
        for(int i=0;i<top;++i)ans+=my[sta[i]].distance(my[sta[i+1]]);
        ans+=my[sta[0]].distance(my[sta[top]]);
        printf("%.0f\n",ans);
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值