题意:给定二维平面上不超过50000个点,求最远点对距离的平方。
思路:由数据量来判断直接枚举会超时。注意到最远距离点对必出现在这些点的凸包上,所以可以先求出凸包,然后在凸包上枚举。此法的最坏情况复杂度仍然是n^2的,但是可以AC这道题了。在复杂度意义下的优化是旋转卡壳(参考http://www.cppblog.com/staryjy/archive/2009/11/19/101412.html):
如果qa,qb是凸包上最远两点,必然可以分别过qa,qb画出一对平行线。通过旋转这对平行线,我们可以让它和凸包上的一条边重合,如图中蓝色直线,可以注意到,qa是凸包上离p和qb所在直线最远的点。于是我们的思路就是枚举凸包上的所有边,对每一条边找出凸包上离该边最远的顶点,计算这个顶点到该边两个端点的距离,并记录最大的值。直观上这是一个O(n2)的算法,和直接枚举任意两个顶点一样了。但是注意到当我们逆时针枚举边的时候,最远点的变化也是逆时针的,这样就可以不用从头计算最远点,而可以紧接着上一次的最远点继续计算(详细的证明可以参见上面链接中的论文)。于是我们得到了O(n)的算法。
凸包上枚举:
#include
#include
#include
#include
#include
#include
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 50005
struct point{
int x,y;
}p[N];
int n;
int stack[N],top = -1;
int multi(struct point a,struct point b,struct point c){
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
int dis(struct point a,struct point b){
return (b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y);
}
int cmp(struct point a,struct point b){
int tmp = multi(p[1],a,b);
if(tmp == 0)
return dis(p[1],a) < dis(p[1],b);
return tmp>0;
}
int main(){
int i,j,res=0;
struct point begin;
scanf("%d",&n);
begin.x = begin.y = 10005;
for(i = 1;i<=n;i++){
scanf("%d %d",&p[i].x,&p[i].y);
if(p[i].y < begin.y){
begin = p[i];
j = i;
}else if(p[i].y==begin.y && p[i].x0 && multi(p[stack[top-1]], p[stack[top]], p[i])<=0)
top--;
stack[++top] = i;
}
for(i = 0;i
旋转卡壳:
#include
#include
#include
#include
#include
#include
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 50005
struct point{
int x,y;
}p[N];
int n;
int stack[N],top = -1;
int multi(struct point a,struct point b,struct point c){
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
int dis(struct point a,struct point b){
return (b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y);
}
int cmp(struct point a,struct point b){
int tmp = multi(p[1],a,b);
if(tmp == 0)
return dis(p[1],a) < dis(p[1],b);
return tmp>0;
}
int main(){
int i,j,res=0;
struct point begin;
scanf("%d",&n);
begin.x = begin.y = 10005;
for(i = 1;i<=n;i++){
scanf("%d %d",&p[i].x,&p[i].y);
if(p[i].y < begin.y){
begin = p[i];
j = i;
}else if(p[i].y==begin.y && p[i].x0 && multi(p[stack[top-1]], p[stack[top]], p[i])<=0)
top--;
stack[++top] = i;
}//至此用graham法求得凸包,下面是旋转卡壳法
j = 1;
stack[++top] = 1;
for(i = 0;i multi(p[stack[i]], p[stack[i+1]], p[stack[j]]))
j = (j+1)%top;//逆时针枚举距离线段(p[stack[i]],p[stack[i+1]])最远的点j
res = max(res,dis(p[stack[i]],p[stack[j]]));
}
printf("%d\n",res);
}