【模板】闵可夫斯基和
闵可夫斯基和,是两个欧几里得空间的点集的和,以德国数学家闵可夫斯基命名。
点集A与B的闵可夫斯基和就是{o|o=a+b},其中a属于A,b属于B。
对于凸包这种特殊的图形,它的闵可夫斯基和有一些较好的性质。
比如:凸包之间的闵可夫斯基和一定是凸包。
求凸包之间的闵可夫斯基和的方法。
把两个凸包的每一条向量都抠出来,按照极角序排序构成新凸包即可。
注意点和向量的去重(向量相同斜率去重)。
还有个地方可以提一下:求多个凸包的闵可夫斯基和的时候可以直接全把边拿出来一块求,没有必要两个两个求。
具体实现的时候,找出最高且最靠左的点。
先把这个点加入答案,从这个点开始把所有向量遍历一遍,最后去掉最后一个点即可(最后这个点会和第一个点重合)。
下面是C++的代码实现:
pot P={-inf,-inf},Q={-inf,-inf},R={-inf,-inf};
n=read();
for(int i=1;i<=n;i++)
{
a[i].x=read();a[i].y=read();
if(dcmp(a[i].y-P.y)==0&&dcmp(a[i].x-P.x)<0)P=a[i];
if(dcmp(a[i].y-P.y)>0)P=a[i];
if(i!=1)f[++cnt]=a[i]-a[i-1];if(i==n)f[++cnt]=a[1]-a[i];
}
n=read();
for(int i=1;i<=n;i++)
{
b[i].x=read();b[i].y=read();
if(dcmp(b[i].y-Q.y)==0&&dcmp(b[i].x-Q.x)<0)Q=b[i];
if(dcmp(b[i].y-Q.y)>0)Q=b[i];
if(i!=1)f[++cnt]=b[i]-b[i-1];if(i==n)f[++cnt]=b[1]-b[i];
}
n=read();
for(int i=1;i<=n;i++)
{
c[i].x=read();c[i].y=read();
if(dcmp(c[i].y-R.y)==0&&dcmp(c[i].x-R.x)<0)R=c[i];
if(dcmp(c[i].y-R.y)>0)R=c[i];
if(i!=1)f[++cnt]=c[i]-c[i-1];if(i==n)f[++cnt]=c[1]-c[i];
}
sort(f+1,f+cnt+1,cmp);
pot k=P+Q+R;p[++tot]=k;
for(int i=1;i<=cnt;i++)
{
k=k+f[i];
if(i!=cnt&&dcmp(f[i].x*f[i+1].y-f[i].y*f[i+1].x)==0)continue;
p[++tot]=k;
}
tot--;k=p[1];
应用:
1.判断两个凸包是否相交
两个凸包相交的条件:存在a=b,a属于A,b属于B。
也就是存在a-b=0
对B取反后和A计算一下闵可夫斯基和,若包含点(0,0)则说明原方程优借,即两个凸包有交。
2.给出三个凸包,每次询问一个点,问能否从三个凸包中各选出一个点并连接成三角形,使得三角形的重心在询问点上。
解:
考虑重心的表达式。设三角形的三个顶点的坐标分别为(x1,y1),(x2,y2),(x3,y3),那么重心一定位于((x1+x2+x3)/3,(y1+y2+y3)/3)。
发现这个表达式显然是一个闵可夫斯基和的形式,因此,直接求出三个凸包的闵可夫斯基和然后按比例缩小一下即可。