NYOJ-253LK的旅行【凸包+旋转卡壳】

LK的旅行

时间限制: 2000 ms  |  内存限制: 65535 KB
难度: 5
描述
LK最近要去某几个地方旅行,她从地图上计划了几个点,并且用笔点了出来,准备在五一假期去这几个城市旅行。现在希望你找出她点的所有的点中距离最远的两个点的距离是多少。各个景点可以认为是在一个平面上。

输入
第一行有一个整数0<n<10表示测试数据的组数随后的n组数据中,第一行有一个整数3<m<100000表示有m个旅游景点。随后的m行每行有两个整数,分别表示每一个点的x和y。景点坐标中可能有重复的,0<=x,y<=10000)
输出
每组数据输出距离最远的点对的距离的平方.
样例输入
1
4
0 0
1 1
0 1
1 0
样例输出
2

这是个的凸包问题,最远点对是凸包的某两个顶点,本来我是求完顶点集之后枚举。但是OJ给的m的数据量比较大,所以会超时O(N*2)。发现还要结合一个算法,即旋转卡壳法。
友情链接:

我觉得是讲的挺好的博客

凸包问题:http://www.cnblogs.com/jbelial/archive/2011/08/05/2128625.html

旋转卡壳法:http://blog.csdn.net/duanxian0621/article/details/8058009

#include <stdio.h>
#include <stdlib.h>
#define INF 999999
struct Node{
	int x,y;
};
int count;
int Max(int a,int b,int c)
{
	if(a<b)
		a=b;
	if(a<c)
		a=c;
	return a;
}
int com(const void *a,const void *b)
{//二级排序规则
	Node *c;
	Node *d;
	c=(Node *)a;
	d=(Node *)b;
	if(c->x!=d->x)
		return c->x-d->x;
	return c->y-d->y;
}
Node p[100099];
Node v[100099];
int CountCrossProduct(int x1,int y1,int x0,int y0,int x2,int y2)
{//计算叉积
	return (x1-x0)*(y2-y0)-(x2-x0)*(y1-y0);
}
int _CountCrossProduct(Node p1,Node p0,Node p2)
{//计算叉积的绝对值
	int a=(p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
	if(a>=0)
		return a;
	return -a;
}
int Graham()
{//Graham扫描法寻找凸包顶点
	int top=0;//模拟栈
	Node p0,p1,p2;
	p[top++]=v[0];
	p[top++]=v[1];
	p[top++]=v[2];
	for(int i=3;i<count;i++)
	{
		p2=v[i];
		do
		{
			p1=p[--top];
			p0=p[top-1];
		}while(CountCrossProduct(p2.x,p2.y,p0.x,p0.y,p1.x,p1.y)>0);
		p[top++]=p1;
		p[top++]=p2;
	}
	return top;
}
int RCA(int m)
{//旋转卡壳法寻找凸包直径
	int ans=0;
	int i,j=1;
	p[m]=p[0];
	for(i=0;i<m;i++)
	{
		while(_CountCrossProduct(p[i],p[i+1],p[j+1])>_CountCrossProduct(p[i],p[i+1],p[j]))
			j=(j+1)%m;
		ans=Max(ans,(p[j].x-p[i].x)*(p[j].x-p[i].x)+(p[j].y-p[i].y)*(p[j].y-p[i].y),(p[j].x-p[i+1].x)*(p[j].x-p[i+1].x)+(p[j].y-p[i+1].y)*(p[j].y-p[i+1].y));
		//更新最长距离
	}
	return ans;
}
int main()
{
	int n;
	int m;
	int i;
	scanf("%d",&n);
	while(n--)
	{
		count=0;
		scanf("%d",&m);
		for(i=0;i<m;i++)
		{scanf("%d%d",&p[i].x,&p[i].y);}
		qsort(p,m,sizeof(Node),com);//按x由小到大,其次y由小到大排序
		v[count++]=p[0];//p[0]为最左下的点
		p[m].x=INF;p[m].y=INF;
		//下面两个for循环实现以p0为原点,逆时针进栈
		for(i=1;i<m;i++)//下半部分点按X从小往大进栈,相同x取y最小值
		{
			if(p[i].x==p[i-1].x)
				continue;
			if(p[i].y<p[0].y)
				v[count++]=p[i];
		}
		for(i=m-1;i>=1;i--)//上半部分点按x从大往小进栈,相同x取y最大值
		{
			if(p[i].x==p[i+1].x)
				continue;
			if(p[i].y>p[0].y)
				v[count++]=p[i];
		}
		m=Graham();
		printf("%d\n",RCA(m));
	}
	return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值