闵可夫斯基和算法(时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn))

附:二分判断点是否在凸包内(时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn))


  • 官方定义:两个图形 A , B A,B A,B的闵可夫斯基和 C = ( a + b ∣ a ∈ A , b ∈ B ) C=(a+b|a∈A,b∈B) C=(a+b∣a∈A,b∈B)
  • 通俗一点:从原点向图形A内部的每一个点做向量,将图形B沿每个向量移动,所有的最终位置的并便是闵可夫斯基和(具有交换律)

详细思路见博客:

  • 闵可夫斯基和

核心代码:

point v1[N], v2[N]; // 记录两个凸包的向量集
struct polygon{point P[N];int n, m;int sta[N];void andrew(){sort(P, P + n);n = unique(P, P + n) - P;m = 0;for(int i=0; i<n; i++)       //先求下凸包{while(m > 1 && sign(area(P[sta[m - 2]], P[sta[m - 1]], P[i])) <= 0) m--;sta[m++] = i;}int v = m;for(int i=n - 2; i>=0; i--)  //再求上凸包{while(m > v && sign(area(P[sta[m - 2]], P[sta[m - 1]], P[i])) <= 0) m--;sta[m++] = i;}if(n > 1) m--;              // 删去末尾多余的起始点,最后凸包是闭合的}void get_vector(point * v){     // 得到两个凸包的向量集for(int i=0; i<m; i++) v[i] = P[sta[(i + 1) % m]] - P[sta[i]];}void minkowski(polygon & A, polygon & B) // 计算凸包A和凸包B的闵可夫斯基和,并记录在此结构体{A.get_vector(v1); B.get_vector(v2);n = 0;P[n++] = A.P[A.sta[0]] + B.P[B.sta[0]];int l1 = 0, l2 = 0;while(l1 < A.n && l2 < B.n) P[n] = P[n - 1] + (cross(v1[l1], v2[l2]) > 0 ? v1[l1++] : v2[l2++]), n++;while(l1 < A.n) P[n] = P[n - 1] + v1[l1++], n++;while(l2 < B.n) P[n] = P[n - 1] + v2[l2++], n++;andrew();}bool in_convex(point p)                 // 二分判断点p是否在凸包内,时间复杂度O(nlogn){int l = 1, r = m - 2;while(l <= r){int mid = l + r >> 1;double a1 = area(P[sta[0]], P[sta[mid]], p);double a2 = area(P[sta[0]], P[sta[mid + 1]], p);if(sign(a1) >= 0 && sign(a2) <= 0){if(sign(area(P[sta[mid]], P[sta[mid + 1]], p)) >= 0)return 1;return 0;}else if(sign(a1) < 0)r = mid - 1;elsel = mid + 1;}return 0;}
}A, B, C;
  • main()函数中的计算如下所示
int main()
{A.andrew(); B.andrew();  // 先求A和B的凸包C.minkowski(A, B);      // 再把A和B的闵可夫斯基和记录在C中,求一下凸包
}



一道例题

P4557 [JSOI2018]战争

AC代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>using namespace std;//基本定义
const double pi = acos(-1);
const double eps = 1e-8;
const int N = 2e5 + 10;
int dcmp(double x, double y){if(fabs(x - y) < eps) return 0;if(x < y) return -1;return 1;
}
int sign(double x)
{if(fabs(x) < eps) return 0;if(x < 0) return -1;return 1;
}//点,向量
struct point{double x, y;bool operator < (const point a) const {if(dcmp(x, a.x)) return dcmp(x, a.x) < 0;return dcmp(y, a.y) < 0;}bool operator == (const point a) const { return !dcmp(x, a.x) && !dcmp(y, a.y); }
};point operator + (point a, point b) { return {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) { return {a.x - b.x, a.y - b.y}; }
point operator * (point a, double p) { return {a.x * p, a.y * p}; }
point operator / (point a, double p) { return {a.x / p, a.y / p}; }//向量运算
double dot(point a, point b) { return a.x * b.x + a.y * b.y; }
double cross(point a, point b) { return a.x * b.y - b.x * a.y; }
double length(point a) { return sqrt(dot(a, a)); }
double angle(point a) { return atan2(a.y, a.x); }                           // 返回向量a的极角,值域为(-pi, pi]
double angle(point a, point b) { return acos(dot(a, b) / length(a) / length(b)); } // 返回向量a,b的夹角
double area(point a, point b, point c) { return cross(b - a, c - a); }      // 返回a->b->c的三角形的有向面积
point rotate(point a, double rad) { return {a.x * cos(rad) - a.y * sin(rad), a.x * sin(rad) + a.y * cos(rad)}; } // rad为弧度 且为逆时针旋转的角double project(point a, point b) { return dot(a, b) / length(b); }          // 返回向量a再向量b上的投影
point norm(point a) { return a / length(a); }
double get_dis(point a, point b) { return length(a - b); }bool on_segment(point a, point b, point c){          // 判断平面上的点c是否在线段ab上return sign(cross(b - a, c - a)) == 0 && sign(dot(c - a, c - b)) <= 0;
}point v1[N], v2[N];
struct polygon{point P[N];int n, m;int sta[N];void input(){for(int i=0; i<n; i++) scanf("%lf %lf", &P[i].x, &P[i].y);}void andrew(){sort(P, P + n);n = unique(P, P + n) - P;m = 0;for(int i=0; i<n; i++)      //先求下凸包{while(m > 1 && sign(area(P[sta[m - 2]], P[sta[m - 1]], P[i])) <= 0) m--;sta[m++] = i;}int v = m;for(int i=n - 2; i>=0; i--) //再求上凸包{while(m > v && sign(area(P[sta[m - 2]], P[sta[m - 1]], P[i])) <= 0) m--;sta[m++] = i;}if(n > 1) m--;              // 删去末尾多余的起始点,最后凸包是闭合的}void get_vector(point * v){for(int i=0; i<m; i++) v[i] = P[sta[(i + 1) % m]] - P[sta[i]];}void minkowski(polygon & A, polygon & B){A.get_vector(v1); B.get_vector(v2);n = 0;P[n++] = A.P[A.sta[0]] + B.P[B.sta[0]];int l1 = 0, l2 = 0;while(l1 < A.n && l2 < B.n) P[n] = P[n - 1] + (cross(v1[l1], v2[l2]) > 0 ? v1[l1++] : v2[l2++]), n++;while(l1 < A.n) P[n] = P[n - 1] + v1[l1++], n++;while(l2 < B.n) P[n] = P[n - 1] + v2[l2++], n++;// for(int i=0; i<n; i++) cout<<"# "<<P[i].x<<" "<<P[i].y<<endl;// for(int i=0; i<A.n; i++) cout<<"## "<<v1[i].x<<" "<<v1[i].y<<endl;// for(int i=0; i<B.n; i++) cout<<"### "<<v2[i].x<<" "<<v2[i].y<<endl;andrew();}bool in_convex(point p){int l = 1, r = m - 2;while(l <= r){int mid = l + r >> 1;double a1 = area(P[sta[0]], P[sta[mid]], p);double a2 = area(P[sta[0]], P[sta[mid + 1]], p);if(sign(a1) >= 0 && sign(a2) <= 0){if(sign(area(P[sta[mid]], P[sta[mid + 1]], p)) >= 0)return 1;return 0;}else if(sign(a1) < 0)r = mid - 1;elsel = mid + 1;}return 0;}
}A, B, C;int main()
{int q; scanf("%d %d %d", &A.n, &B.n, &q);A.input(); B.input();for(int i=0; i<B.n; i++) B.P[i].x = -B.P[i].x, B.P[i].y = -B.P[i].y;A.andrew(); B.andrew();C.minkowski(A, B);// for(int i=0; i<C.m; i++) cout<<C.P[C.sta[i]].x<<" "<<C.P[C.sta[i]].y<<endl;while(q--){point p; scanf("%lf %lf", &p.x, &p.y);if(C.in_convex(p)) printf("1\n");else printf("0\n");}return 0;
}

【计几】闵可夫斯基和 二分判断点是否在凸包内相关推荐

  1. [JSOI2018] 战争(闵可夫斯基和 + 二分)

    题意 给出两个凸包 A,BA,BA,B,有 qqq 次询问,每次询问给出向量 (dx,dy)(dx,dy)(dx,dy),问 BBB 所有点移动 (dx,dy)(dx,dy)(dx,dy) 是否与 A ...

  2. Gym 100883J palprime(二分判断点在凸包里)

    题意:判断一堆小点有多少个在任意三个大点构成的三角形里面. 思路:其实就是判断点在不在凸包里面,判断的话可以使用二分来判断,就是判断该点在凸包的哪两个点和起点的连线之间. 代码: 1 /** @xig ...

  3. 如何判断一个点在多边形内

    一.从三角形开始说起---怎么判断一个点在三角形内 三角形是最简单的多边形了.先说说三角形有哪些判断方法. 参考自:判断一个点是否在三角形内部 - 知乎 几种方法判断平面点在三角形内_独L无二的博客- ...

  4. 利用叉乘快速判断点是否在三角形内

    利用叉乘快速判断点是否在三角形内 判断P是否在三角形ABC内: 向量: pa = P - A,  ca = C - A; ba = B - A; 如果向量 cross(ca, pa)与向量 cross ...

  5. 【Unity3D自学记录】判断物体是否在镜头内

    判断物体是否在镜头内. 其实很简单的方法 代码如下: [csharp] view plaincopy using UnityEngine; using System.Collections; publ ...

  6. 判断点是否处于多边形内的三种方法(转)

    1. 叉乘判别法(只适用于凸多边形)想象一个凸多边形,其每一个边都将整个2D屏幕划分成为左右两边,连接每一边的第一个端点和要测试的点得到一个矢量v,将两个2维矢量扩展成3维的,然后将该边与v叉乘,判断 ...

  7. 判断点是否在凸多边形内

    文章目录 判断点是否在凸多边形内 1,原理 2.右手坐标系 3.向量叉积 补充知识: 可以理解利用向量的叉积,很容易判定一个多边形的凹凸性.也可以判定点是否在多边形的内部. - 判定多边形的凹凸性 判 ...

  8. 判断网页是否为微信内置浏览器打开?

    文章目录 (两种方法)教你:"如何判断网页是不是在微信端内置浏览器打开?" 本文根据项目开发实际情况,着重探讨在微信内置浏览器中调用支付功能,遇到的几个坑! 目的: (方法一)全部 ...

  9. js 日期比较大小,js判断日期是否在区间内,js判断时间段是否在另外一个时间段内...

    /** * 日期解析,字符串转日期 * @param dateString 可以为2017-02-16,2017/02/16,2017.02.16 * @returns {Date} 返回对应的日期对 ...

最新文章

  1. van Emda Boas
  2. 吴恩达《机器学习》课程总结(15)异常检测
  3. html5 酒店入住插件,jQuery酒店类入住日期时间范围选择器插件
  4. 总结获取原生JS(javascript)的父节点、子节点、兄弟节点
  5. 常用 Http 的请求方法
  6. 60个高质量的CSS、XHTML网页布局模板下载
  7. 10张劲爆眼球的科学动图
  8. CentOS 7 搭建swagger Api文档管理系统
  9. 在Ubuntu 18.04系统上安装Java 11的方法
  10. 搭建顶级域名下的个人博客网站
  11. oracle test 卡死,oracle11g plsql调试存储过程卡死的处理技巧
  12. linux进程map,linux内存优化一文中 查看进程mem_map 的实现
  13. Pycharm 新版本打开md文件卡死-解决办法
  14. 部署calico网络的k8s集群
  15. 网站三级域名是什么样?
  16. amoeba mysql_Amoeba介绍及Amoeba和mysql proxy有区别
  17. 查询分析器默认代码颜色
  18. 工作站压力测试软件,胜任多种工作负载 联想P500工作站评测
  19. 课程在线学习的小程序
  20. labspec6教程_LabSpec6软件功能–光谱扫描方式-Horiba.PDF

热门文章

  1. apidemos -views/lists/Activate items 学习
  2. 比百度还好用的搜索功能,支持「多条件」搜索哟
  3. 结构阻尼比的4种常用测量方法概述
  4. nvidia linux 黑屏,Ubuntu卡logo、卡住、黑屏无法正常启动、屏幕和键盘背光无法调节等一系列问题的罪恢祸首:NVIDIA显卡驱动...
  5. 知乎不能改用户名吗_知乎上改了用户名 知乎不能改用户名吗
  6. 计算机毕业设计(附源码)python自动出卷考试系统
  7. 补贴大战,烧不出社区团购的未来
  8. java高清截屏_java截屏简单例子
  9. 网页刷新代码(html)两种方式
  10. matlab中的互感线圈模型,分析不同线圈的互感系数