您的位置:首页 > 其它

poj 2187 旋转卡壳(平面上最远点对)

2015-08-04 16:05 183 查看
题意:给定二维平面上不超过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 <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 50005
struct point{
int x,y;
}p
;
int n;
int stack
,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].x<begin.x){
begin = p[i];
j = i;
}
}
p[j] = p[1];
p[1] = begin;
sort(p+2,p+n+1,cmp);
stack[++top] = 1;
stack[++top] = 2;
for(i = 3;i<=n;i++){
while(top>0 && multi(p[stack[top-1]], p[stack[top]], p[i])<=0)
top--;
stack[++top] = i;
}
for(i = 0;i<top;i++)
for(j = i+1;j<=top;j++)
res = max(res,dis(p[stack[i]],p[stack[j]]));
printf("%d\n",res);

}


旋转卡壳:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 50005
struct point{
int x,y;
}p
;
int n;
int stack
,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].x<begin.x){
begin = p[i];
j = i;
}
}
if(n==2){
printf("%d\n",dis(p[1],p[2]));
return 0;
}
p[j] = p[1];
p[1] = begin;
sort(p+2,p+n+1,cmp);
stack[++top] = 1;
stack[++top] = 2;
for(i = 3;i<=n;i++){
while(top>0 && 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<top;i++){
while(multi(p[stack[i]],p[stack[i+1]],p[stack[j+1]]) > 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);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: