凸包与旋转卡壳

关于凸包的几个名词:

1.支撑线:

如果一条直接L通过凸多边形P的一个顶点,且多边形在这条直线的一侧,则称L是P的支撑线

2.对踵点:

如果过凸包上的两个点可以画一对平行线,使得凸包上左右的点都夹在两条平行线之间或者

落在平行线上那么两个点称为一对对踵点。两条平行的支撑线所过的两点就是一对对踵点。可以

证明一个凸n边形的对踵点最多有3n/2对

3.凸多边形的直径:

将一个多边形上任意两点间的距离的最大值定义为多边形的直径。确定这个直径的点对数可能

多于一对。

旋转卡壳求直径的步骤:

step1. 计算多边形y方向上的端点,ymin,ymax.

step2. 通过ymin,ymax,狗找两条水平的切线,由于它们已经是一对对踵点,计算它们之间的距

离并维护一个当前的最大值。

step3. 同时旋转两条之间到其中一条于多边形的一条边重合。

step4. 一个新的对踵点产生,计算新的距离,并和当前的最大值做比较,若大于当前最大值则

更新。

step5. 重复操作3,4知道产生对踵点对(ymin,ymax).

分析:旋转卡壳的复杂度为O(n),求凸包的复杂度为O(nlogn).

题目链接:传送门

求凸包的直径的平方

代码如下:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn = 5e4+10;

typedef struct Point{
    int x,y;
    bool operator <(const struct Point &B)const{
        if(x==B.x)  return y<B.y;
        return x<B.x;
    }
    bool operator ==(const struct Point &B)const{
        return (x-B.x)==0&&(y-B.y)==0;
    }
}Vector;

Vector p[maxn],st[maxn];

int Cross(Point a,Point b,Point o)        {
    return (a.x - o.x) * (b.y - o.y) - (b.x - o.x) * (a.y - o.y);
}

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

int dis(Point a,Point b){
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
//旋转卡壳求凸包的宽度
double rotating_calipers(Point *p,int n){
    int k = 1;
    double ans = 0x7FFFFFFF;
    p[n] = p[0];
    for(int i=0;i<n;i++){
        while(fabs(cross(p[i],p[i+1],p[k])) < fabs(cross(p[i],p[i+1],p[k+1])))
             k = (k+1) % n;
        double tmp = fabs(cross(p[i],p[i+1],p[k]));
        double d   = dist(p[i],p[i+1]);
        ans = min(ans,tmp/d);
    }
    return ans;
}
int main()
{
    int n;
    while(~scanf("%d",&n)){
        for(int i=0;i<n;i++){
            scanf("%d%d",&p[i].x,&p[i].y);
        }
        int m = ConvexHull(p,n,st);
        int ans = rotatint_calipers(st,m);
        printf("%d\n",ans);
    }
    return 0;
}

题目链接:传送门

题意:

平面上n个点,求从这n个点所能组成的最大的三角形的面积。

分析:

首先求凸包。最大的三角形的顶点一定在凸包上,枚举顶点i,j,k,j=i+1,k=j+1.很明显

在枚举k的时候我们可以固定i,j这两个点,然后找到当前能组成的最大的面积的点k.,而且k

的变化和j有关,因此不用从头开始枚举。

我们循环执行k+1,知道area(i,j,k+1)<area(i,j,k),这个时候更新最大的面积,然后可以

旋转j,k,如果area(i,j,k)<area(i,j,k+1) k!=i,则k++,否则跳出,更新面积,j++,旋转一周可

以得到最大的三角形面积,时间复杂度O(n^2)

代码如下:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;

const int maxn = 5e4+10;

const double eps = 1e-10;

int dcmp(double x){
    if(fabs(x)<=0) return 0;
    if(x>0) return 1;
    else return -1;
}

typedef struct Point{
    double x,y;
    bool operator <(const struct Point &B)const{
        if(x==B.x)  return y<B.y;
        return x<B.x;
    }
    bool operator ==(const struct Point &B)const{
        return dcmp(x-B.x)==0&&dcmp(y-B.y)==0;
    }
}Vector;

Vector p[maxn],st[maxn];

double Cross(Point a,Point b,Point o)        {
    return (a.x - o.x) * (b.y - o.y) - (b.x - o.x) * (a.y - o.y);
}

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

double dis(Point a,Point b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

double rotating_calipers(Point *p,int n){
    double ans = 0;
    int j,k;
    for(int i=0;i<n;i++){
        j=(i+1)%n;
        k=(j+1)%n;
        while(fabs(Cross(p[i+1],p[k],p[i]))<fabs(Cross(p[i+1],p[k+1],p[i])))
            k=(k+1)%n;
        while(j!=i&&k!=i){
            ans=max(ans,fabs(Cross(p[i],p[k],p[j])));
            while(fabs(Cross(p[i],p[k],p[j]))<fabs(Cross(p[i],p[(k+1)%n],p[j])))
                k=(k+1)%n;
            j=(j+1)%n;
        }
    }
    return ans/2.0;
}

int main()
{
    int n;
    while(~scanf("%d",&n)){
        if(n==-1) break;
        for(int i=0;i<n;i++)
            scanf("%lf%lf",&p[i].x,&p[i].y);
        int m = ConvexHull(p,n,st);
        //cout<<"m "<<m<<endl;
        if(m<3) puts("0.00");
        if(m==3) printf("%.2lf\n",fabs(Cross(st[0],st[1],st[2]))/2.0);
        else{
            double ans = rotating_calipers(st,m);
            printf("%.2lf\n",ans);
        }
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-09-28 12:02:59

凸包与旋转卡壳的相关文章

算法复习——凸包加旋转卡壳(poj2187)

题目: Description Bessie, Farmer John's prize cow, has just won first place in a bovine beauty contest, earning the title 'Miss Cow World'. As a result, Bessie will make a tour of N (2 <= N <= 50,000) farms around the world in order to spread goodwill

NYOJ_253:LK的旅行(旋转卡壳入门)

题目链接 求平面最大点对. 找凸包 -> 根据凸包运用旋转卡壳算法求最大点对(套用kuang巨模板) 关于旋转卡壳算法 #include<bits/stdc++.h> using namespace std; struct point { int x,y; point operator -(const point& rhs)const { point ret; ret.x=x-rhs.x; ret.y=y-rhs.y; return ret; } int operator *(c

旋转卡壳部分模板

凸包直径 旋转卡壳凸包直径详解 //计算凸包直径,输入凸包ch,顶点个数为n,按逆时针排列,输出直径的平方 int rotating_calipers(int n) { int q = 1; int ans = 0; ch[n] = ch[0]; for(int i = 0 ; i < n; i++) { while(mul(ch[i+1],ch[q+1],ch[i])>mul(ch[i+1],ch[q],ch[i]))//枚举凸包一条边并扫描其它点,通过计算三角形面积的方法找到最远的点 q

【最小矩形面积覆盖:凸包+旋转卡壳】UVA 10173 Smallest Bounding Rectangle

[最小矩形面积覆盖:凸包+旋转卡壳]UVA 10173 Smallest Bounding Rectangle 题目链接:UVA 10173 Smallest Bounding Rectangle 题目大意 给你n个点,求能够覆盖所有点集的最小矩形面积. 笔者的第2道凸包题目,凸包 + 旋转卡壳,实现点集的最小矩形面积覆盖问题 ">=0"写成"<=0"坑了我一下午!QAQ 说一下思路 ①Graham's Scan法构建凸包,时间复杂度O(nlogn) ②

HDU 5251 矩形面积(二维凸包旋转卡壳最小矩形覆盖问题) --2015百度之星题目

B - 矩形面积 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Description 小度熊有一个桌面,小度熊剪了很多矩形放在桌面上,小度熊想知道能把这些矩形包围起来的面积最小的矩形的面积是多少. Input 第一行一个正整数 T,代表测试数据组数(),接下来 T 组测试数据. 每组测试数据占若干行,第一行一个正整数 ,代表矩形的数量.接下来 N 行,每行 8

UVA 4728 Squares(凸包+旋转卡壳)

题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=17267 [思路] 凸包+旋转卡壳 求出凸包,用旋转卡壳算出凸包的直径即可. [代码] 1 #include<cstdio> 2 #include<vector> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 struct Pt { 8

模板 凸包 旋转卡壳

模板  凸包  旋转卡壳 lrj <训练指南> P272 对于个点按照 x 从小到大排序,再按照 y 点从小到大排序,删除重复的点后,得到序列 p0,p1,p2..., 把 p0 和 p1 放入凸包. 从p2开始,当新点在凸包“前进”方向的左边时继续,否则依次删除最近加入凸包的点,直到新点在左边 PS:判断用叉积即可 1 /******************************************** 2 计算凸包,输入点数组 p, 个数为 n , 输出点数组 ch. 函数返回凸包顶

POJ 3608 两凸包最近距离 旋转卡壳

Bridge Across Islands Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8071   Accepted: 2364   Special Judge Description Thousands of thousands years ago there was a small kingdom located in the middle of the Pacific Ocean. The territory

模板 旋转卡壳 凸包

模板 旋转卡壳 凸包 好早以前看的,现在再记下来吧,当做复习一遍. 那么,先提一下最基本最暴力的求凸包直径的方法吧---枚举...好吧..很多问题都可以用 枚举 这个“万能”的方法来解决,过程很简单方便是肯定的,不过在效率上就要差很远了.  要求一个点集的直径,即使先计算出这个点集的凸包,然后再枚举凸包上的点对,这样来求点集直径的话依然会在凸包上点的数量达到O(n)级别是极大的降低它的效率,也浪费了凸包的优美性质.不过在数据量较小或者很适合时,何必要大费周折的用那些麻烦复杂的算法呢,枚举依然是解