旋转卡壳

一般套路:对散点跑一个凸包出来,然后在凸包上进行旋转卡壳。

例如:(旋转卡壳求两凸包最小距离完整代码)

#include<map>
#include<set>
#include<ctime>
#include<stack>
#include<cmath>
#include<queue>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<complex>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100010;
const int maxm = 400010;
const double eps = 1e-8;
const double inf = 1e10;
int Min( int a , int b ){ return a<b?a:b; }
int Max( int a , int b ){ return a>b?a:b; }
//套路区
int sgn( double x )
{
    if ( fabs(x)<eps ) return 0;
    if ( x<0 ) return -1;
    if ( x>0 ) return  1;
}
struct Point
{
    double x,y;
    Point(){}
    Point( double _x , double _y )
    {
        x = _x; y = _y;
    }
    Point operator -( const Point&b )const
    {
        return Point( x-b.x , y-b.y );
    }
    double operator ^( const Point&b )const
    {
        return x*b.y-y*b.x;
    }
    double operator *( const Point&b )const
    {
        return x*b.x+y*b.y;
    }
    void input()
    {
        scanf ( "%lf%lf" , &x , &y );
    }
};
struct Line
{
    Point s,e;
    Line(){}
    Line( Point _s , Point _e )
    {
        s = _s; e = _e;
    }
};
double dist( Point a , Point b )
{
    return sqrt((a-b)*(a-b));
}
Point List[maxn],Stack[maxn]; int Top;
bool cmp( Point a , Point b )
{
    double tmp = (a-List[0])^(b-List[0]);
    if ( tmp>0 ) return true;
    else if ( tmp==0&&sgn( dist( a , List[0] )-dist( b , List[0] ) )<=0 ) return true;
    else return false;
}
void Graham( int n )
{
    int k = 0;
    for ( int i=1 ; i<n ; i++ )
        if ( List[i].y<List[k].y||(List[i].y==List[k].y&&List[i].x<List[k].x) ) k = i;
    swap ( List[k] , List[0] );
    sort ( List+1  , List+n , cmp );
    if ( n==1 )
    {
        Top = 1;
        Stack[0] = List[0];
        return;
    }
    if ( n==2 )
    {
        Top = 2;
        Stack[0] = List[0];
        Stack[1] = List[1];
        return;
    }
    Stack[0] = List[0];
    Stack[1] = List[1];
    Top = 2;
    for ( int i=2 ; i<n ; i++ )
    {
        while ( Top>1&&((Stack[Top-1]-Stack[Top-2])^(List[i]-Stack[Top-2]))<=0 ) Top--;
        Stack[Top++] = List[i];
    }
}
//套路区
//卡壳算法区
//点到线段的距离,返回点到线段最近的点
Point NearestPointToLineSeg( Point P , Line L )
{
    Point result;
    double t = ((P-L.s)*(L.e-L.s))/((L.e-L.s)*(L.e-L.s));
    if ( t>=0&&t<=1 )
    {
        result.x = L.s.x+(L.e.x-L.s.x)*t;
        result.y = L.s.y+(L.e.y-L.s.y)*t;
    }
    else
    {
        if( dist(P,L.s)<dist(P,L.e) ) result = L.s;
        else result = L.e;
    }
    return result;
}
//点p0到线段p1p2的距离
double PointtoSeg( Point p0 , Point p1 , Point p2 )
{
    return dist( p0 , NearestPointToLineSeg( p0 , Line( p1 , p2 ) ) );
}
//平行线段p0p1和p2p3的距离
double dispallseg( Point p0 , Point p1 , Point p2 , Point p3 )
{
    double ans1 = min( PointtoSeg( p0 , p2 , p3 ) , PointtoSeg( p1 , p2 , p3 ) );
    double ans2 = min( PointtoSeg( p2 , p0 , p1 ) , PointtoSeg( p3 , p0 , p1 ) );
    return min( ans1 , ans2 );
}
//向量a1a2和b1b2的位置关系
double Get_angle( Point a1 , Point a2 , Point b1 , Point b2 )
{
    return (a2-a1)^(b1-b2);
}
//两凸包最小距离旋转卡壳
double Rotating_calipers( Point p[] , int np , Point q[] , int nq )
{
    int sp = 0,sq = 0;
    for ( int i=0 ; i<np ; i++ )
        if ( sgn( p[i].y-p[sp].y )<0 ) sp = i;
    for ( int i=0 ; i<nq ; i++ )
        if ( sgn( q[i].y-q[sq].y )>0 ) sq = i;
    double tmp;
    double ans = dist( p[sp] , q[sq] );
    for ( int i=0 ; i<np ; i++ )
    {
        while ( sgn( tmp = Get_angle( p[sp] , p[(sp+1)%np] , q[sq] , q[(sq+1)%nq] ) )<0 ) sq = ( sq+1 )%nq;
        if ( sgn(tmp)==0 )  ans = min( ans , dispallseg( p[sp] , p[(sp+1)%np] , q[sq] , q[(sq+1)%nq] ) );
        else ans = min( ans , PointtoSeg( q[sq] , p[sp] , p[(sp+1)%np] ) );
        sp = ( sp+1 )%np;
    }
    return ans;
}
double solve( Point p[] , int np , Point q[] , int nq )
{
    return min( Rotating_calipers( p , np , q , nq ) , Rotating_calipers( q , nq , p , np ) );
}
Point p[maxn],q[maxn];
//卡壳算法区
//实现区
int main()
{
    for ( int n,m ; scanf ( "%d%d" , &n , &m )==2 ; )
    {
        if  ( n==0&&m==0 ) break;
        for ( int i=0 ; i<n ; i++ ) List[i].input();
        Graham( n ); n = Top;
        for ( int i=0 ; i<n ; i++ ) p[i] = Stack[i];
        for ( int i=0 ; i<m ; i++ ) List[i].input();
        Graham( m ); m = Top;
        for ( int i=0 ; i<m ; i++ ) q[i] = Stack[i];
        printf ( "%.4lf\n" , solve( p , n , q , m ) );
    }
    return 0;
}
//实现区

求解平面最远点对卡壳算法:

double Rotating_calipers( Point p[] , int n )
{
    double ans = 0;
    Point v;
    int cur = 1;
    for ( int i=0 ; i<n ; i++ )
    {
        v = p[i]-p[(i+1)%n];
        while ( sgn(v^(p[(cur+1)%n]-p[cur]))<0 ) cur = ( cur+1 )%n;
        ans = max ( ans , max ( dist2( p[i] , p[cur] ) , dist2( p[(i+1)%n] , p[(cur+1)%n] ) ) );
    }
    return ans;
}

求解平面点集最大三角形:

double Rotating_calipers( Point p[] , int n )
{
    double ans = 0;
    for ( int i=0 ; i<n ; i++ )
    {
        int j = (i+1)%n;
        int k = (j+1)%n;
        while ( j!=i&&k!=i )
        {
            ans = max ( ans , fabs((p[j]-p[i])^(p[k]-p[i])) );
            while ( sgn((p[i]-p[j])^(p[(k+1)%n]-p[k]))<0 ) k = (k+1)%n;
            j = (j+1)%n;
        }
    }
    return ans;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值