欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-04-01 15:36:47
...

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

例题:

这几个题打了我一下午.
洛谷 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());
  }
}

好了.祝大家好运.