准备和约定
pdd operator - (pdd a,pdd b) 重载坐标减法
{
return {a.x - b.x, a.y - b.y};
}
double cross(pdd a,pdd b) 叉积(外积)
{
return (a.x*b.y)-(a.y*b.x);
}
double area(pdd a,pdd b,pdd c) 求两条向量叉积
{
return cross(b-a,c-a);
}
double get_dist(pdd a,pdd b) 获取向量模长
{
double dx= a.x-b.x;
double dy= a.y-b.y;
return sqrt(dx*dx+dy*dy);
}
一,Andrew 凸包
- 用一个凸多边形包括图中所有的点
- 性质:周长保证最小,面积不一定
- 实现:Andrew的思路基于分治,构建凸包的上链和下链分别处理
概述
1,利用夹角,让整个图形保持左转。先将最左边的前两个点加入栈中,每次加入新点时判断是否左拐(叉积大于0),如果是就将新点直接加入;如果不是,就弹出栈顶,直到左拐,将新点加入栈中。
2,注意,栈中要保证至少有一个元素,也就是top>=2的时候才可以弹出栈顶
3,一遍求解上凸包,反着一遍求解下凸包
流程
1,将所有点进行快排,以x为第一关键字,y为第二关键字升序排序
2,将第一个点放入栈中,【这个点一定时凸包的最左边的点了,是不会清理掉的】,然后在将第二个点放入栈中。当栈中元素大于等于2的时候,就要判断栈顶元素是否还要保留。
如果新点在栈顶元素和次栈顶元素所组成的直线的右侧,那么,直接将新点加入栈中。
如果新点在栈顶元素和次栈顶元素所组成的直线的左侧,那么,将栈顶元素不断弹出,直到新点的位置出现在栈顶元素与次栈顶元素所在直线的右侧结束。
那么,我们这个过程,是从左往右走的,而且每次找的点都是在当前直线的右侧,也就是直线的下方向,那么我们得到的凸包就是我们的下半部分。
求上半部分的时候,从右往左排就自然而然是对的了。
code
- 注意:凸包可能退化为一条直线,这时候要避免一直弹栈使得凸包长度为
0
- 不想凸包边上有点:把 area 改成
<=
判断弹栈
double andrew()
{
sort(q, q + n);
int stk[N];
int top = 0;
for (int i = 0; i < n; i ++ )
{
while (top >= 2 && area(q[stk[top - 1]], q[stk[top]], q[i]) <= 0)
top -- ;
stk[ ++ top] = i;
}
for (int i = n - 2 ; i >= 0 ; i -- )
{
while (top >= 2 && area(q[stk[top - 1]], q[stk[top]], q[i]) < 0)
top -- ;
stk[ ++ top] = i;
}
double res = 0;
for (int i = 2; i <= top; i ++ )
res += get_dist(q[stk[i - 1]], q[stk[i]]);
return res;
}
二,旋转卡壳
问题描述:求解平面上距离最远的一对点的距离
求解思路:
- 1,可能的最远点一定在凸包上(暴力:枚举任意凸包上两个点求解距离)
- 2,引入两条平行线,同时逼近凸包的时候一定可以被凸包上的点卡住(保证相切,这一对点为对踵点),我们发现这一对点就是针对这条直线下的最远点对(暴力:枚举所有的直线斜率并卡壳)
- 3,角度是密集的不能枚举,思考离散,针对旋转中的直线,只有和凸包上一条边重合之后,才会改变对踵点对,那么只要求得针对每条边的最远距离点就可以了
- 4,对于每一条边,他上面的点与他的距离是单峰的,那么就有单调性,前面直线的对踵点一定不是后面直线的对踵点,那么就是双指针
约定和细节:
1,top
是栈顶,从-1
开始表示没有值,最后是凸包上点的个数
2,每条边链接两点都可能和这条边的对踵点组成对踵点对,那么直接都算一次取得最大值
3,top--
表示删去重复的起点
4,(j+1)%top
表示环形是由于多边形是闭合环形
5,注意特判退化为链的凸包直接去取得最远点对
void get_convex()
{
sort(p,p+n);
top = -1;
rep(i,0,n-1)
{
while(top>=2 && area(p[stk[top-1]],p[stk[top]],p[i]) <= 0)
{
if(area(p[stk[top-1]],p[stk[top]],p[i]) < 0)used[stk[top--]]=0;
else top--;
}
stk[++top]=i;
used[i]=1;
}
used[0]=0;
frep(i,n-1,0)
{
if(used[i])continue;
while(top>=2 && area(p[stk[top-1]],p[stk[top]],p[i]) <= 0) top--;
stk[++top]=i;
}
top--;
}
int spin_calipers(){
if(top<=2) return get_dist(p[0],p[n-1]);
int res = 0;
for(int i=0,j=2;i<top;i++)
{
auto d = p[stk[i]],e=p[stk[i+1]];
while (area(d,e,p[stk[j]]) < area(d,e,p[stk[j+1]])) j=(j+1)%top;
res = max(res, max(get_dist(d, p[stk[j]]), get_dist(e, p[stk[j]])));
}
return res;
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d%d", &p[i].x, &p[i].y);
get_convex();
printf("%d\n", spin_calipers());
return 0;
}