平面最近点对&平面最远点对 分治(枚举),旋转卡壳

这两个也是经典题了,没想到用的方法竟然大相径庭.

例题:

这几个题打了我一下午.
洛谷 1429 平面最近点对(加强版)
Codevs 2319 最近最远点对
Vijos 1012 清帝之惑之雍正
POJ 2187&&UESTC 752&&OpenJ_Bailian 2187 Beauty Contest

题解

平面最近点对

说说是分治,实际上并不用.

/*
首先把所有的点按照x坐标排序.
然后确定一个目前的最近距离,扫的时候如果两点横坐标之差已经大于最短间距,就直接跳出循环,后面不可能会有更优解.复杂度玄学,但是不会T.
*/
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define pii pair<int,int>
#define yi first
#define er second
using namespace std;
typedef long long ll;
const int boss=2e5;
pii p[boss+10];int n;//用n个pair存储点

namespace fastio{//输入和输出优化
#pragma GCC optimize("inline")
#define ll long long
double ten[]={1,1e1,1e2,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13,1e14,1e15,1e16,1e17,1e18,1e19};
int read(){int x=0,f=1;char c=getchar();for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;for (;isdigit(c);c=getchar()) x=x*10+c-'0';return x*f;}
int write(int x){if (!x) return putchar('0');int bit[20],i,p=0;for (;x;x/=10) bit[++p]=x%10;for (i=p;i;--i) putchar(bit[i]+48);}
void read(double &r){double x=0,t=0;int s=0,f=1;char c=getchar();for (;!isdigit(c);c=getchar()){if (c=='-') f=-1;if (c=='.') goto readt;}for (;isdigit(c)&&c!='.';c=getchar()) x=x*10+c-'0';readt:for (;c=='.';c=getchar());for (;isdigit(c);c=getchar()) t=t*10+c-'0',++s;r=(x+t/ten[s])*f;}
void read(int &x){x=0;char c=getchar();for (;!isdigit(c);c=getchar());for (;isdigit(c);c=getchar()) x=x*10+c-'0';}
int dwrite(ll x){if (x==0) return putchar(48);int bit[20],p=0,i;for (;x;x/=10) bit[++p]=x%10;for (i=p;i>0;--i) putchar(bit[i]+48);}
void write(double x,int k=6){static int n=ten[k];if (x==0) {putchar('0'),putchar('.');for (int i=1;i<=k;++i) putchar('0');return;}if (x<0) putchar('-'),x=-x;ll y=(ll)(x*n)%n;x=(ll)x;dwrite(x),putchar('.');int bit[20],p=0,i;for (;p<k;y/=10) bit[++p]=y%10;for (i=p;i>0;--i) putchar(bit[i]+48);}
#undef ll
};
using namespace fastio;

ll sqr(int x){return 1ll*x*x;}//平方
ll dist(int x,int y){return sqr(p[x].yi-p[y].yi)+sqr(p[x].er-p[y].er);}//两点间距离(没有开根号)

int main()
{
int i,j;
n=read();
for (i=1;i<=n;i++) p[i].yi=read(),p[i].er=read();
sort(p+1,p+n+1);//按照横坐标排序
ll ans=dist(1,2);
for (i=1;i<=n;++i)
  {
  for (j=i+1;j<=n&&sqr(p[i].yi-p[j].yi)<ans;j++)
    {
    ans=min(ans,dist(i,j));//枚举
    }
  } 
printf("%.4lf",sqrt(ans));//输出的时候开根避免精度问题
}

平面最远点对

用的是旋转卡壳(qia ke)法.
首先把凸包求出来.要是不知道凸包,度娘上找一找.

/*
核心就是向量叉积的运用:如果叉积大于0,两个向量成逆时针;小于0成顺时针;等于0共线.
用两条直线卡在凸包的两侧,然后一个一个找,就能有最大值.
*/
#pragma GCC optimize("inline",3)
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;

namespace fastio{
#define ll long long
double ten[]={1,1e1,1e2,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13,1e14,1e15,1e16,1e17,1e18,1e19};
int read(){int x=0,f=1;char c=getchar();for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;for (;isdigit(c);c=getchar()) x=x*10+c-'0';return x*f;}
int write(int x){if (!x) return putchar('0');int bit[20],i,p=0;for (;x;x/=10) bit[++p]=x%10;for (i=p;i;--i) putchar(bit[i]+48);}
void read(double &r){double x=0,t=0;int p=0,f=1;char c=getchar();for (;!isdigit(c);c=getchar()){if (c=='-') f=-1;if (c=='.') goto readt;}for (;isdigit(c)&&c!='.';c=getchar()) x=x*10+c-'0';readt:for (;c=='.';c=getchar());for (;isdigit(c);c=getchar()) t=t*10+c-'0',++p;r=(x+t/ten[p])*f;}
void read(int &x){x=0;char c=getchar();for (;!isdigit(c);c=getchar());for (;isdigit(c);c=getchar()) x=x*10+c-'0';}
int dwrite(ll x){if (x==0) return putchar(48);int bit[20],p=0,i;for (;x;x/=10) bit[++p]=x%10;for (i=p;i>0;--i) putchar(bit[i]+48);}
void write(double x,int k=6){static int n=ten[k];if (x==0) {putchar('0'),putchar('.');for (int i=1;i<=k;++i) putchar('0');return;}if (x<0) putchar('-'),x=-x;ll y=(ll)(x*n)%n;x=(ll)x;dwrite(x),putchar('.');int bit[20],p=0,i;for (;p<k;y/=10) bit[++p]=y%10;for (i=p;i>0;--i) putchar(bit[i]+48);}
#undef ll
};

namespace vectors{
const double pi=3.141592653587,eps=1e-10;
struct xl{
double x,y;xl(){x=0,y=0;}
void read1(){scanf("%lf%lf",&x,&y);}
void read2(){double a,b;scanf("%lf%lf%lf%lf",&x,&y,&a,&b);x=a-x,y=a-y;}
void print(){printf("%lf %lf",x,y);}
double mo(){return x*x+y*y;}//sqrt(x*x+y*y);}
};
int cmp(xl a,xl b){return a.y!=b.y?a.y<b.y:a.x<b.x;}
xl operator -(xl a){xl b;b.x=-a.x,b.y=-a.y;return b;}
xl operator +(xl a,xl b){xl c;c.x=a.x+b.x,c.y=a.y+b.y;return c;}
xl operator -(xl a,xl b){return a+-b;}
xl operator *(xl a,double b){xl c;c.x=a.x*b,c.y=a.y*b;return c;}
double operator *(xl a,xl b){return a.x*b.x+a.y*b.y;}
double jiao(xl a,xl b){return acos(a*b/a.mo()/b.mo());}
double cross(xl a,xl b){return a.x*b.y-a.y*b.x;}
int operator ==(xl a,xl b){return fabs(a.x-b.x)<=eps&&fabs(a.y-b.y)<=eps;}
double mianji(xl a,xl b,xl c){return fabs(cross(b-a,c-a))/2;}
xl zhuan(xl a,double du){xl b;b.x=a.x*cos(du)-a.y*sin(du),b.y=a.x*sin(du)+a.y*cos(du);return b;}
int shx(xl a,xl b,xl p){if (a.x<b.x) swap(a,b);double ans=cross(p-a,b-a);return abs(ans)<=eps?0:ans>0?1:-1;}
};
using namespace vectors;
using namespace fastio;

typedef long long ll;
const int boss=2e5;
xl p[boss+10];int n,tubao[boss+10]={0,1,2},num;

ll sqr(int x){return 1ll*x*x;}
double dist(xl x,xl y){xl z;z.x=x.x-y.x,z.y=x.y-y.y;return z.mo();}
int cmp2(xl a,xl b)
{
a=a-p[1],b=b-p[1];
double t=cross(a,b);
return t>0||t==0&&a.mo()<b.mo();
}

int graham()//凸包上点的个数
{
int top=2,i;
sort(p+2,p+n+1,cmp2);
for (i=3;i<=n;i++) 
  {
  for (;top>1&&cross(p[tubao[top]]-p[tubao[top-1]],p[i]-p[tubao[top]])<=0;--top);//顺时针方向,不能要
  tubao[++top]=i;
  }
return top;
} 

double qiake()//旋转卡壳
{
double dis=0;int j=3;
p[num+1]=p[1],p[num+2]=p[2];
for (int i=1;i<=num;i++) 
  {
  for (;cross(p[i+1]-p[i],p[j]-p[i])<cross(p[i+1]-p[i],p[j+1]-p[i]);)
    if (++j>num) j-=num;//枚举到每一条边最远的顶点
  dis=max(dis,max((p[i]-p[j]).mo(),(p[i+1]-p[j]).mo()));
  }
return dis;
}

int main()
{
int i,j;
for (;~scanf("%d",&n);)
  {
  for (i=1;i<=n;i++) p[i].read1();
  sort(p+1,p+n+1,cmp);
  num=graham();
  for (i=1;i<=num;++i) p[i]=p[tubao[i]];
  printf("%.0lf\n",qiake());
  }
}

好了.祝大家好运.

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值