一般套路:对散点跑一个凸包出来,然后在凸包上进行旋转卡壳。
例如:(旋转卡壳求两凸包最小距离完整代码)
#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;
}