Gym - 101606B 旋转卡壳

题意:

  • 给定n个点,求凸边形宽的最小值

  • 凸边形的宽指的是如同用游标卡尺卡着多边形的宽度

这里写图片描述

图中蓝线即宽;

引入概念:旋转卡壳

  • 名称来源于算法的过程就像用游标卡尺卡着凸包旋转一周一样。

最小宽度算法思想:

  • 1.计算多边形 y 方向上的端点。 我们称之为 ymin 和 ymax
    2.通过 ymin 和 ymax 构造两条水平切线。 由于他们已经是一对对踵点, 计算他们之间的距离并维护为一个当前最大值。
    3.同时旋转两条线直到其中一条与多边形的一条边重合。
    4.一个新的对踵点对此时产生。 计算新的距离, 并和当前值比较, 并更新。
    5.重复步骤3和步骤4的过程直到 再次产生对踵点对 (ymin,ymax) 。

算法的一点优化:

  • (1)如果这样直接去实现不太好实现,我们可以转化一下,就是这一对平行线上在旋转的过程中我们可以让其中一条与凸包的一条边重合,此时另一条线的顶点是距这条边最远的点。我们可以依次枚举每条边求得距离这条最远的顶点,然后计算这个顶点到边两个端点的距离并取较大的一个,枚举完成时得到的最大距离即为直径长度。但是这样算的话因为枚举了所有边,每次又要枚举所有顶点,因此时间复杂度为O(n^2)。
    (2)但是可以想到,当枚举的边逆时针旋转时,最远点也是跟着逆时针变化,这样我们可以不用每次枚举所有的顶点,直接从上次的最远点开始继续计算即可,此时复杂度为O(n)。

题解:

  • 先计算凸包,在利用旋转卡壳的思想求解

代码:

#include <iostream>
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
double EPS=1e-10;

double add(double a,double b)
{
    if(abs(a+b)<EPS*(abs(a)+abs(b))) return 0;
    return a+b;
}

struct point{
    double x,y;
    point(){}
    point(double x,double y):x(x),y(y){
    }
    point operator +(point p)
    {
        return point(add(x,p.x),add(y,p.y));
    }
    point operator -(point p)
    {
        return point(add(x,-p.x),add(y,-p.y));
    }
    point operator *(double d)
    {
        return point(x*d,y*d);
    }
    double dot(point p)
    {
        return add(x*p.x,y*p.y);
    }
    double det(point p)
    {
        return add(x*p.y,-y*p.x);
    }
};
double cross_product(point x,point y,point z)
{
    return (z-x).det(z-y);
}
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_caliper(vector<point> v)
{
    double min_dis = INF;
    int n = v.size();
    v.push_back(v[0]);
    for (int i = 0; i < n; ++i)
    {
        double res=0;
        for(int j=0;j<n;j++)
        {                                                                             
            res=max(res,cross_product(v[i],v[i+1],v[j])/dis(v[i],v[i+1]));
        }                                                                
        min_dis = min(min_dis,res);                                             
     }                                                                                
     return min_dis;                                                                  
/*****************************************************************************************************/
/***                           优化的思路 但是莫名tle了                                           ***/
/**    int j = 2;                                                                                  ***/
/**    for (int i = 0; i < n; ++i)                                                                 ***/
/**    {                                                                                           ***/
/**      while (cross_product(v[i], v[i + 1], v[j]) < cross_product(v[i], v[i + 1], v[j + 1]))     ***/
/**      {                                                                                         ***/
/**          j = (j + 1) % n;                                                                      ***/
/**      }                                                                                         ***/
/**       min_dis = min(min_dis,cross_product(v[i],v[i+1],v[j])/dis(v[i],v[i+1]) );                ***/
/**    }                                                                                           ***/
/*****************************************************************************************************/
}

/****************/
/**  计算凸包  **/
/****************/

bool cmp(const point& p,const point& q)
{
    if(p.x!=q.x) return p.x<q.x;
    return p.y<q.y;
}
vector<point> convex_hull(point *ps,int n)
{
    sort(ps,ps+n,cmp);
    int k=0;
    vector<point> qs(n*2);
    for(int i=0;i<n;i++)
    {
        while(k>1&&(qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0) k--;
        qs[k++]=ps[i];
    }
    for(int i=n-2,t=k;i>=0;i--)
    {
        while(k>t&& (qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0) k--;
        qs[k++]=ps[i];
    }
    qs.resize(k-1);
    return qs;
}
point ps[105];
int main()
{
    int n;
    cin>>n;
    int cnt=0;
    for(int i=0;i<n;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        ps[cnt++]=point(a,b);
    }
    vector<point> qs=convex_hull(ps,n);
    double res=rotating_caliper(qs);
    printf("%.10f\n",res);
    return 0;
}

望大佬不吝赐教……

文章部分内容来源:
http://blog.csdn.net/wang_heng199/article/details/74477738
http://blog.csdn.net/u012328159/article/details/50809014

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值