闵可夫斯基和

闵可夫斯基和

神奇的玩意儿。

计算几何基础知识:https://www.cnblogs.com/nightsky05/p/16211639.html

就讲了点这篇的前置,真.基础!

定义

在几何中,点集 \(A\)\(B\) 的闵可夫斯基和为:

\[S=\{a+b|a\in A,b\in B\} \]

性质

\(A\)\(B\) 闵可夫斯基和的凸包相当于 \(A\) 的凸包与 \(B\) 的凸包的闵可夫斯基和。

证明可以看看这篇博客,有图(因为我懒得画图了):闵可夫斯基和

这个性质显然可以扩展。

定义 \(A+B\) 表示 \(A\)\(B\) 的闵可夫斯基和,\(Conv(A)\) 表示 \(A\) 的凸包。

有:

\[Conv(\sum S)=\sum Conv(S) \]

正题

如何求两个凸包的闵可夫斯基和。

将两个凸包的边按极角排序后一一相连即可。

证明我不咋会,感性理解画下图会发现两个凸包的边恰好在它们的闵可夫斯基和的凸包中出现一次。

而凸包相当于是极角排序后相邻的边一一相连得到的。

所以我们将两个凸包的边并入一个集合排序即可。

我们用向量来表示点,那么边也同样可以用向量来表示。

一个凸包我们就可以用其中一个点加上凸包上的边来描述(那个点是用来确定凸包的位置的)

下面称这个点叫:基础点

两个凸包做闵可夫斯基和后,新的凸包的基础点就应该是 \((x_1+x_2,y_1+y_2)\)\((x_1,y_1),(x_2,y_2)\) 是原来俩凸包的基础点。

根据一个凸包的边集和基础点我们可以确定这个凸包的端点。我们将凸包的边按极角排序。然后依次遍历这些边,并且让基础点加上描述这些边的变量,每加一条边就得到一个新的端点的信息。

值得注意的是,边的极角排序的方式是根据你选的基础点来的。

举个例子。

假设我们是按 \(x\) 为第一关键字,\(y\) 为第二关键字,然后用单调栈求得凸包,那么 \(st_1\) 就应该是下凸壳最左边点,所以我们按极角排序时,就应该是让 \(y\) 的负半轴作为极轴。

说点形象的就是终边在第三象限的大于终边在第一象限的大于终边在第四象限的。

Opr2uj.png

也就是极角大小该按箭头方向递增。

因为我们基础点选的下凸壳最左边的,那么为了逆时针的遍历下一个点的话,应该找一条指向右下的向量(如果你想顺时针遍历就得反一反)。

实在不行感性理解一下

板子

[USACO22JAN] Multiple Choice Test P


这题的意思就是给了我们 \(n\) 个点集。求 \(\sum S\) 中离原点距离最大的那个。

根据凸包的定义可知,一个点集内所有点必然在其凸包内。

所以我们要求的答案一定在凸包上

又由三角形不等式可知,在凸包边上的点一定没有作为凸包端点的点优。

所以我们只要将凸包求出,然后统计凸包的端点即可。

凸包的求法,叉积等基础计算几何知识在开头的博客中有讲。

代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 2e5+5;
struct Array
{
	ll x,y;
	Array(ll a=0,ll b=0){x=a;y=b;}
	Array operator + (const Array &b)const{return Array{x+b.x,y+b.y};}
	Array operator - (const Array &b)const{return Array{x-b.x,y-b.y};}
	ll operator * (const Array &b)const{return x*b.y-y*b.x;}
	ll cal(){return x*x+y*y;}
	bool operator < (const Array &b)const{return x!=b.x?x<b.x:y<b.y;}
	bool dir(){return x>0||(x==0&&y>0);}
}S,e[MAXN];
vector<Array> A,vec;
bool used[MAXN];
int n,tot,st[MAXN];
void Andrew()
{
	int top=0,siz=A.size();
	sort(A.begin(),A.end());
	st[++top]=0;
	for(int i=0;i<siz;++i) used[i]=0;
	for(int i=1;i<siz;++i)
	{
		while(top>=2&&(A[st[top]]-A[st[top-1]])*(A[i]-A[st[top]])<=0)
			used[st[top--]]=0;
		used[i]=1;
		st[++top]=i;
	}
	int tmp=top;
	for(int i=siz-2;i>=0;--i)
	{
		if(!used[i])
		{
			while(top>tmp&&(A[st[top]]-A[st[top-1]])*(A[i]-A[st[top]])<=0)
				used[st[top--]]=0;
			used[i]=1;
			st[++top]=i;
		}
	}
	for(int i=1;i<=top;++i) vec.push_back(A[st[i]]);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		int G=0;scanf("%d",&G);
		A.clear();vec.clear();
		for(int j=1;j<=G;++j)
		{
			Array x;
			scanf("%lld %lld",&x.x,&x.y);
			A.push_back(x);
		}
		Andrew();
		S=S+vec[0];
		for(int i=1;i<vec.size();++i) e[++tot]=vec[i]-vec[i-1];
	}
	sort(e+1,e+1+tot,[&](Array x,Array y){return x.dir()!=y.dir()?x.dir():x*y>0;});
	ll Ans=S.cal();
	for(int i=1;i<=tot;++i)
		S=S+e[i],Ans=max(Ans,S.cal());
	printf("%lld\n",Ans);
	return 0;
}
posted @ 2022-04-30 22:34  夜空之星  阅读(563)  评论(1编辑  收藏  举报