您的位置:首页 > 其它

POJ 2187 平面最远点对 凸包入门

2017-09-25 00:24 459 查看
点击打开链接题目大意: 给出n个点,求出最远的两个点之间的距离思路:由于数据范围很大,暴力肯定不可以了,考虑平面的最远点对一定在凸包上(这点可以反证法来证明)。那么我们求出凸包上所有的点然后枚举凸包上的点就可以了.这里采用Graham扫描算法,复杂度nlogn.
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
const int MAXN = 50000 + 5;

struct P{ int x, y; };
P p[MAXN];
int n;
//求两点之间的距离
int dis(P a, P b)
{
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
/*计算叉积
判断点和直线的方向用叉积
若P1×p2 >0 则p1在p2的顺时针方向.
<0 p1在p2的逆时针方向
= 0 共线  */
int cross(P a, P b, P c)
{
return (a.x - c.x) * (b.y - c.y) - (b.x - c.x) * (a.y - c.y);
}
//极角排序
bool cmp(P a, P b)
{
int x = cross(p[0],a,b);
if(x > 0 || (x == 0 && dis(a, p[0]) < dis(b, p[0]))) return 1;
return 0;
}

vector<P> Graham()
{
vector<P> stk(n);//存凸包
int k = 0;
//Graham 算法
for (int i = 0; i < n; i++) {
while (k > 1 && (cross(p[i], stk[k-2], stk[k-1])) <= 0)
k--;
stk[k++] = p[i];
}
stk.resize(k);//调整正确的凸包顶点数
return stk;
}

int main()
{
while (~scanf("%d", &n)) {
int ans = 0, rp = 0;//rp用于临时保存最左下点的下标
//输入
for (int i = 0; i < n; i++) {
scanf("%d%d", &p[i].x, &p[i].y);
if(p[rp].y > p[i].y
|| (p[rp].y == p[i].y && p[rp].x > p[i].x))//找最左下的点
rp = i;
}

//极角排序
swap(p[rp], p[0]);//初始化,方便cmp过程
sort(p + 1, p + n, cmp);

//Graham Scan构成凸包
vector<P> stk = Graham();

//暴力求解
for (int i = 0; i < stk.size(); i++) {
for (int j = 0; j < i; j++)
ans = max(ans, dis(stk[i], stk[j]));
}

cout<<ans<<endl;
}

return 0;
}
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=5e4+5;
struct node
{
int x,y;
}a[maxn],sta[maxn];
//求亮点之间距离的平方.
int dis(node n1,node n2)
{
return (n1.x-n2.x)*(n1.x-n2.x)+(n1.y-n2.y)*(n1.y-n2.y);
}

//计算叉积
int det(int x1,int y1,int x2,int y2)
{
return x1*y2-x2*y1;
}
//判断点和直线的方向用叉积
//若P1×p2 >0 则p1在p2的顺时针方向.
//<0 p1在p2的逆时针方向
//= 0 共线
int cross(node a,node n1,node n2)
{
return det(n1.x-a.x,n1.y-a.y,n2.x-a.x,n2.y-a.y);
}
//极角排序
bool cmp(node n1,node n2)
{
//a[0]为纵坐标最小的点一定在凸包上,以它为原点进行极角排序
int k = cross(a[0],n1,n2);
if(k > 0) return 1;//角度小的排在前面
//若共线则距离a[0]距离近的在前面.
if(k == 0 && dis(a[0],n1) < dis(a[0],n2))
return 1;
return 0;
}

void solve(node *ch,int len)
{
int ans = 0;
for(int i = 0;i <= len;++i)
{
for(int j = i + 1;j <= len;++j)
{
ans = max(ans,dis(ch[i],ch[j]));
}
}
cout<<ans<<endl;
}
//最后栈中存的点就是凸包上的点.
void Graham(int n)
{
int i,head;
for(i = 1;i < n;++i) //先找到最下面的那个点作为原点.
if(a[i].x < a[0].x || (a[i].x == a[0].x && a[i].y < a[0].y))
swap(a[0],a[i]);
sort(a+1,a+n,cmp);//极角排序
a
= a[0];
sta[0] = a[0];//最下面的一个点和第二个点一定在凸包上.
sta[1] = a[1];
sta[2] = a[2];
head = 2;
//Graham 算法
for(int i = 3;i < n;++i)
{
while(head >= 2)
{
int fu = cross(sta[head - 1],sta[head],a[i]);
if(fu < 0)
head--;
else if(fu == 0 && dis(sta[head - 1],a[i]) > dis(sta[head - 1],sta[head]))
head--;
else
break;
}
sta[++head] = a[i];
}
solve(sta,head);
}
int main(){
int n;
while(scanf("%d",&n) > 0)
{
for(int i = 0;i < n;++i)
{
scanf("%d %d",&a[i].x,&a[i].y);
}
Graham(n);
}
return 0;
}

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