旋转卡壳小结

这几天做了两道旋转卡壳题,做个小结。

第一题POJ2187

本题求n个点的最远点对。

首先n^2枚举肯定TLE。

考虑其他方法。

应当注意到最远点对必然在凸包上,所以首先做凸包。

接着枚举每条边,可以利用高单峰的性质,维护一个指针。然后就可以求离每条边最远的点对了。

虽然很简单,但是未能1A。

2个错误:
凸包第二个for要从n-1到1.不能到2.
取最大值手滑。

/*
ID:huangta3
PROG:
LANG:C++
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <list>
#include <queue>
#include <vector>
#include <ctime>
#include <set>
#include <bitset>
#include <deque>
#include <fstream>
#include <stack>
#include <map>
#include <utility>
#include <cassert>
#include <string>
#include <iterator>
#include <cctype>
using namespace std;
const int maxn=50003;
int get()
{
    int f=0,v=0;char ch;
    while(!isdigit(ch=getchar()))if(ch=='-')break;
    if(ch=='-')f=1;else v=ch-48;
    while(isdigit(ch=getchar()))v=v*10+ch-48;
    if(f==1)return -v;else return v;
}

struct Tpoint
{
    int x,y;
    Tpoint(){}
    Tpoint(int _x,int _y){x=_x,y=_y;}
    void getpoint(){x=get(),y=get();}
}a[maxn],P[maxn];
int n;

bool cmp(Tpoint a,Tpoint b){return a.x==b.x?a.y<b.y:a.x<b.x;}
bool operator ==(Tpoint a,Tpoint b){return a.x==b.x&&a.y==b.y;}
Tpoint operator -(Tpoint a,Tpoint b){return Tpoint(a.x-b.x,a.y-b.y);}
int operator *(Tpoint a,Tpoint b){return a.x*b.y-a.y*b.x;}
int dist(Tpoint a,Tpoint b){return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);}
void init()
{
    n=get();
    for(int i=1;i<=n;i++)a[i].getpoint();
    sort(a+1,a+1+n,cmp);
}

void graham()
{
    int h=1,r=0;
    for(int i=1;i<=n;i++)
    {
        if(i!=1&&a[i]==a[i-1])continue;
        while(h<r&&(P[r]-P[r-1])*(a[i]-P[r])<=0)r--;
        P[++r]=a[i];
    }
    h=r;
    for(int i=n-1;i>=1;i--)
    {
        if(a[i]==a[i+1])continue;
        while(h<r&&(P[r]-P[r-1])*(a[i]-P[r])<=0)r--;
        P[++r]=a[i];
    }
    n=r-1;
}

void rc()
{
    int ans=dist(P[1],P[2]);
    for(int i=1,j=2;i<=n;i++)
    {
        int k=i%n+1;
        while(abs((P[i]-P[j])*(P[k]-P[j]))<abs((P[i]-P[j%n+1])*(P[k]-P[j%n+1])))j=j%n+1;
        ans=max(ans,max(dist(P[i],P[j]),dist(P[j],P[k])));
    }
    printf("%d\n",ans);
}

int main()
{
    init();
    graham();
    rc();
    return 0;
}



第二题POJ3608

本题求的是两个不相交凸包的最近距离。

先保证两个凸包定点逆时针。设j为凸包A纵坐标最小的点,k为凸包B纵坐标最大的点。

首先逆时针枚举凸包A上的每条边(通过移动指针j,即使得直线与当前枚举的边重合),同时移动指针k(即维护两条直线平行,通过叉积算面积来判断),直至指针L转了一圈。然后再反过来枚举凸包B上的每条边(移动指针R),适当地移动指针L,再转一圈。

然后在求距离的时候要分情况

一种是求点到线段的最近距离,另一种是求线段和线段的最近距离。而第二种情况可以转化为四个点到线段的最近距离。

点到线段的最近距离我是用距离判投影再用面积法做的。

本题在poj用g++提交63ms,rank3。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <list>
#include <string>
using namespace std;
const int maxn=10003;
const double eps=1e-10;
int get()
{
    int f=0,v=0;char ch;
    while(!isdigit(ch=getchar()))if(ch=='-')break;
    if(ch=='-')f=1;else v=ch-48;
    while(isdigit(ch=getchar()))v=v*10+ch-48;
    if(f==1)return -v;else return v;
}
double getd()
{
    double d=0,d2=0,d3=1; char ch; bool flag=0;
    while(!isdigit(ch=getchar()))if(ch=='-')break;
    if(ch=='-')flag=true;else d=ch-48;
	while(isdigit(ch=getchar()))d=d*10+ch-48;
    if(ch=='.')
    {
        while(isdigit(ch=getchar()))d2=d2*10+ch-48,d3=d3*0.1;
        d+=d3*d2;
    }
    if(flag)return -d;else return d;
}

int sig(double x)
{
    if(fabs(x)<=eps)return 0;
    return x>eps?1:-1;
}

struct Tpoint
{
    double x,y;
    Tpoint(){}
    Tpoint(double _x,double _y){x=_x,y=_y;}
    void getpoint(){x=getd(),y=getd();}
}a[maxn],b[maxn];
int n,m;
double ans;

double dist(Tpoint a){return sqrt(a.x*a.x+a.y*a.y);}
Tpoint operator -(Tpoint a,Tpoint b){return Tpoint(a.x-b.x,a.y-b.y);}
double operator *(Tpoint a,Tpoint b){return a.x*b.y-a.y*b.x;}

void make(Tpoint a[maxn],int n)
{
    for(int i=2;i<n;i++)
    {
        int tp=sig((a[i]-a[i-1])*(a[i+1]-a[i]));
        if(tp==0)continue;
        if(tp<0)reverse(a+1,a+1+n);
        return;
    }
}

double dist_pointseg(Tpoint p,Tpoint a,Tpoint b)
{
    double PA=dist(p-a),PB=dist(p-b),AB=dist(a-b);
    if(min(PA*PA,PB*PB)+AB*AB<max(PA*PA,PB*PB))return min(PA,PB);
    else return fabs((a-p)*(b-p))/AB;
}

double mindist_segseg(Tpoint a,Tpoint b,Tpoint v,Tpoint u)
{
    return min(min(dist_pointseg(a,u,v),dist_pointseg(b,u,v)),min(dist_pointseg(u,a,b),dist_pointseg(v,a,b)));
}

void rc(Tpoint a[maxn],int n,Tpoint b[maxn],int m)
{
    int j=1,k=1;
    double tp;
    for(int i=2;i<=n;i++)if(a[i].y<a[j].y)j=i;
    for(int i=2;i<=m;i++)if(b[i].y>b[k].y)k=i;
    for(int i=1;i<=n;i++,j=j%n+1)
    {
        while(tp=(b[k]-a[j+1])*(a[j]-a[j+1])-(b[k+1]-a[j+1])*(a[j]-a[j+1])<eps)k=k%m+1;
        if(sig(tp)>0)ans=min(ans,dist_pointseg(b[k],a[j],a[j+1]));
        else ans=min(ans,mindist_segseg(a[j],a[j+1],b[k],b[k+1]));
    }
}

int main()
{
    while(n=get(),m=get(),n!=0)
    {
        for(int i=1;i<=n;i++)a[i].getpoint();
        for(int i=1;i<=m;i++)b[i].getpoint();
        make(a,n),make(b,m);
        a[n+1]=a[1],b[m+1]=b[1];
        ans=1e10;
        rc(a,n,b,m),rc(b,m,a,n);
        printf("%.6f\n",ans);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值