1082 AlvinZH的学霸养成记VI

思路

难题,凸包。

分析问题,平面上给出两类点,问能否用一条直线将二者分离。

首先应该联想到这是一个凸包问题,分别计算两类点的凸包,如果存在符合题意的直线,那么这两个凸包(凸多边形)一定是不相交的。

计算凸包一般有两种方法,Graham扫描法和Jarvis步进法。

Graham扫描法比较简单,好理解,书中也有伪代码。先找到最左下点P0,对剩下的点相对P0进行极角排序。然后依次进栈判断。当算法终止时,栈中从底部到顶部,依次是按逆时针方向排列的凸包中的点(有时候需要利用这一点)。

这个方法有一个缺点是如果想求得纯粹的顶点,即不包含共线点,很容易出错。中间的点可以通过叉积直接排除,对栈底部和顶部还需要额外的判断。

  1. bool cmp(const Point& p1,const Point& p2)
  2. {
  3. double C = Cross(p1-p[0], p2-p[0]);
  4. return C ? C>0 : dis(p[0],p1) < dis(p[0],p2);
  5. }
  6. //点集凸包
  7. void Graham(int n)
  8. {
  9. double x=p[0].x;
  10. double y=p[0].y;
  11. int mi=0;
  12. for(int i=1;i<n;i++)//找到最左下点
  13. {
  14. if(p[i].x<x||(p[i].x==x&&p[i].y<y))
  15. {
  16. x=p[i].x;
  17. y=p[i].y;
  18. mi=i;
  19. }
  20. }
  21. Point tmp=p[mi];
  22. p[mi]=p[0];
  23. p[0]=tmp;
  24. sort(p+1,p+n,cmp);//极角排序
  25. stack[0]=p[0];
  26. stack[1]=p[1];
  27. stack[2]=p[2];
  28. int top=2;
  29. for (int i=3 ; i<n ; ++i)
  30. {
  31. while(crossProd(stack[top-1],stack[top],p[i])<=0&&top>=2)
  32. --top;
  33. stack[++top]=p[i];
  34. }
  35. }

解题代码中采用Jarvis步进法,先把点集按先y后x排序,从最低点到最高点遍历,再从最高点到最低点遍历,即可找到凸包上所有的点。算法终止时,数组CH中也是按逆时针顺序排列的,且最后一点与第一点相同。

x、y意义上是相同的,下列代码中采用从左到右、再从右到左,排序也作相应改变,先x后y,效果一样。

  1. bool operator < (const Point& p1, const Point& p2) {
  2. return p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y);
  3. }
  4. //点集凸包
  5. vector<Point> ConvexHull(vector<Point> p) {
  6. //预处理,删除重复点
  7. sort(p.begin(), p.end());
  8. p.erase(unique(p.begin(), p.end()), p.end());
  9. int n = p.size();
  10. int m = 0;
  11. vector<Point> ch(n+1);
  12. for(int i = 0; i < n; i++) {
  13. while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
  14. ch[m++] = p[i];
  15. }
  16. int k = m;
  17. for(int i = n-2; i >= 0; i--) {
  18. while(m > k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
  19. ch[m++] = p[i];
  20. }
  21. if(n > 1) m--;
  22. ch.resize(m);
  23. return ch;
  24. }

第二个问题,判断红点和蓝点分别组成的两个凸包(凸多边形)是否相离。完全暴力判断,别无他法。

①任取一个红点,判断是否在蓝凸包内or上。如果是,则无解。蓝点红凸包同理。

二分法:判断点在凸多边形内,n个点总时间复杂度O(nlgn)。

②任取红凸包上的一条线段和蓝凸包上的一条线段,判断二者是否相交。如果相交(不一定是规范相交,有公共点就算相交),则无解。

方法:叉积的应用。如果另一线段两端点分别在这一线段的两侧,那么线段可能相交(也可能在线段外),否则不可能相交。对另一线段采用相同方法就可判断出是否相交了。

任何一个凸包退化成点或者线段时本来需要特判,上面两种情况已经包含。

分析

求解凸包的两种算法中,Graham扫描法时间复杂度\(O(nlgn),Jarvis步进法时间复杂度为\)O(nh)。

求解凸包方法:http://www.csie.ntnu.edu.tw/~u91029/ConvexHull.html(页面粉粉的

判断相离复杂度较高,分析无意义。

参考代码

  1. #include<cstdio>
  2. #include<vector>
  3. #include<cmath>
  4. #include<algorithm>
  5. using namespace std;
  6. //精度判断
  7. const double eps = 1e-10;
  8. double dcmp(double x) {
  9. if(fabs(x) < eps) return 0;
  10. else return x < 0 ? -1 : 1;
  11. }
  12. struct Point {
  13. double x, y;
  14. Point(double x=0, double y=0):x(x),y(y) {}
  15. };
  16. Point operator - (const Point& A, const Point& B) {
  17. return Point(A.x-B.x, A.y-B.y);
  18. }
  19. double Cross(const Point& A, const Point& B) {
  20. return A.x*B.y - A.y*B.x;
  21. }
  22. double Dot(const Point& A, const Point& B) {
  23. return A.x*B.x + A.y*B.y;
  24. }
  25. bool operator < (const Point& p1, const Point& p2) {
  26. return p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y);
  27. }
  28. bool operator == (const Point& p1, const Point& p2) {
  29. return p1.x == p2.x && p1.y == p2.y;
  30. }
  31. //判断两条线段是否相离
  32. bool SegmentProperIntersection(const Point& a1, const Point& a2, const Point& b1, const Point& b2) {
  33. double c1 = Cross(a2-a1,b1-a1), c2 = Cross(a2-a1,b2-a1),
  34. c3 = Cross(b2-b1,a1-b1), c4=Cross(b2-b1,a2-b1);
  35. return dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0;
  36. }
  37. bool OnSegment(const Point& p, const Point& a1, const Point& a2) {
  38. return dcmp(Cross(a1-p, a2-p)) == 0 && dcmp(Dot(a1-p, a2-p)) < 0;
  39. }
  40. //点集凸包,Jarvis步进法
  41. vector<Point> ConvexHull(vector<Point> p) {
  42. //预处理,删除重复点
  43. sort(p.begin(), p.end());
  44. p.erase(unique(p.begin(), p.end()), p.end());
  45. int n = p.size();
  46. int m = 0;
  47. vector<Point> ch(n+1);
  48. for(int i = 0; i < n; i++) {
  49. while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
  50. ch[m++] = p[i];
  51. }
  52. int k = m;
  53. for(int i = n-2; i >= 0; i--) {
  54. while(m > k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
  55. ch[m++] = p[i];
  56. }
  57. if(n > 1) m--;
  58. ch.resize(m);
  59. return ch;
  60. }
  61. //判断点与凸多边形是否相离
  62. int IsPointInPolygon(const Point& p, const vector<Point>& poly) {
  63. int wn = 0;
  64. int n = poly.size();
  65. for(int i=0; i<n; ++i) {
  66. const Point& p1 = poly[i];
  67. const Point& p2 = poly[(i+1)%n];
  68. if(p1 == p || p2 == p || OnSegment(p, p1, p2)) return -1;//在边界上
  69. int k = dcmp(Cross(p2-p1, p-p1));
  70. int d1 = dcmp(p1.y - p.y);
  71. int d2 = dcmp(p2.y - p.y);
  72. if(k > 0 && d1 <= 0 && d2 > 0) wn++;
  73. if(k < 0 && d2 <= 0 && d1 > 0) wn--;
  74. }
  75. if(wn != 0) return 1;//内部
  76. return 0;//外部
  77. }
  78. bool ConvexPolygonDisjoint(const vector<Point> ch1, const vector<Point> ch2) {
  79. int c1 = ch1.size();
  80. int c2 = ch2.size();
  81. for(int i=0; i<c1; ++i)
  82. if(IsPointInPolygon(ch1[i], ch2) != 0) return false;
  83. for(int i=0; i<c2; ++i)
  84. if(IsPointInPolygon(ch2[i], ch1) != 0) return false;
  85. for(int i=0; i<c1; ++i)
  86. for(int j=0; j<c2; ++j)
  87. if(SegmentProperIntersection(ch1[i], ch1[(i+1)%c1], ch2[j], ch2[(j+1)%c2])) return false;
  88. return true;
  89. }
  90. int main()
  91. {
  92. int n, m;
  93. while(scanf("%d %d", &n, &m) == 2 && n > 0 && m > 0)
  94. {
  95. vector<Point> P1, P2;
  96. double x, y;
  97. for(int i = 0; i < n; i++) {
  98. scanf("%lf %lf", &x, &y);
  99. P1.push_back(Point(x, y));
  100. }
  101. for(int i = 0; i < m; i++) {
  102. scanf("%lf %lf", &x, &y);
  103. P2.push_back(Point(x, y));
  104. }
  105. if(ConvexPolygonDisjoint(ConvexHull(P1), ConvexHull(P2)))
  106. printf("YES\n");
  107. else
  108. printf("NO\n");
  109. }
  110. }

2016级算法第六次上机-F.AlvinZH的学霸养成记VI的更多相关文章

  1. 2016级算法第六次上机-D.AlvinZH的学霸养成记V

    1081 AlvinZH的学霸养成记V 思路 中等题,计算几何. 这是一个排序问题,按极角排序.可以转化为叉积的应用,对于点A和B,通过叉积可以判断角度大小,共线时再判断距离. 叉积的应用.OA × ...

  2. 2016级算法第六次上机-C.AlvinZH的学霸养成记II

    1032 AlvinZH的学霸养成记II 思路 中等题,贪心. 所有课程按照DDL的大小来排序. 维护一个当前时间curTime,初始为0. 遍历课程,curTime加上此课程持续时间d,如果这时cu ...

  3. 2016级算法第五次上机-E.AlvinZH的学霸养成记IV

    1039 AlvinZH的学霸养成记IV 思路 难题,最大二分图匹配. 难点在于如何转化问题,n对n,一个只能攻击一个,判断是否存在一种攻击方案我方不死团灭对方.可以想到把所有随从看作点,对于可攻击的 ...

  4. 2016级算法第五次上机-D.AlvinZH的学霸养成记III

    850 AlvinZH的学霸养成记III 思路 难题.概率DP. 第一种思考方式:直接DP dp[i]:从已经有i个学霸到所有人变成学霸的期望. 那么答案为dp[1],需要从后往前逆推.对于某一天,有 ...

  5. 2016级算法第四次上机-F.AlvinZH的最“长”公共子序列

    940 AlvinZH的最"长"公共子序列 思路 DP,难题. \(dp[i][j]\) :记录A的前i个字符与B的前j个字符变成相同需要的最小操作数. 初始化:dp[i][0] ...

  6. 2016级算法第六次上机-E.Bamboo之吃我一拳

    Bamboo之吃我一拳 分析 当两个点的距离<=d时,才可以出拳,想要使得满足出拳条件的点对最少但不为0 寻找最近点对距离,得到的最近距离能够使得可以出拳的组数最少,因为除了最近点对外其他组合均 ...

  7. 2016级算法第六次上机-B.ModricWang's FFT : EASY VERSION

    1114 ModricWang's FFT EASY VERSION 思路 利用FFT做大整数乘法,实际上是把大整数变成多项式,然后做多项式乘法. 例如,对于\(1234\),改写成\(f(x)=1* ...

  8. 2016级算法第六次上机-A.Bamboo之寻找小金刚

    Bamboo之寻找小金刚 分析 可以抽象为许多连续线段,分别计数左拐和右拐的个数.考察叉积的基础应用. 假设ABC三点构成一个夹角∠ABC,B就是拐点,AC是辅助形成夹角.考虑线段AB和BC形成的向量 ...

  9. 2016级算法第五次上机-F.ModricWang的水系法术

    1066 ModricWang的水系法术 思路 比较典型的最大流问题,需要注意的是,题目已经暗示(明示)了这里的边是双向的,在建图的时候需要加上反向边的容量值. 解决最大流问题的基本思路就是不断在残量 ...

随机推荐

  1. ssh 连接很慢的解决办法

    http://blog.csdn.net/ablo_zhou/article/details/5074887 ============= 现象: 在局域网内,能ping通目标机器,并且时延是微秒级. ...

  2. Java 设计模式系列(十二)策略模式(Strategy)

    Java 设计模式系列(十二)策略模式(Strategy) 策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换.策略模式使得算法可以 ...

  3. 全球顶尖大学的UX课程资源,英文!

    本文由MOCKPLUS团队原创,转载请标明出处,原型设计工具就用更快.更简单的mockplus 要想成为一名优秀的UX设计师,需要掌握很多必备技能.比如,掌握用户体验设计的所有知识和信息架构(易用性方 ...

  4. sqlserver 2017 linux还原windows备份时的路径问题解决

    windows的备份由于路径问题,在Linux上会报错 File 'YourDB_Product' cannot be restored to 'Z:\Microsoft SQL Server\MSS ...

  5. chorme 浏览器记住密码后input黄色背景处理方法(两种)

    使用chrome浏览器选择记住密码的账号,输入框会自动加上黄色的背景,有些设计输入框是透明背景的,需要去除掉这个黄色的背景: 方法1:阴影覆盖 input:-webkit-autofill {   - ...

  6. firefox快速刷新error及解决办法

    问题: 测试发过来bug,说——频繁F5刷新页面,会闪现未加载完样式的页面:    开发用的Chrome,没发现这个问题,测试用的firefox,于是从浏览器的刷新加载机制方面搜索解决办法,没搜到,运 ...

  7. git pull --rebase的使用

    原文:http://www.cnblogs.com/kevingrace/p/5896706.html 使用下面的关系区别这两个操作:git pull = git fetch + git mergeg ...

  8. IntelliJ IDEA 2017版开发SpringBoot之fastJsonHttpMessageConvert使用

    继承WebMvcConfigurerAdapter,改写成自己的json转换工具的写法 1.建立实体类 package com.fastjson; import com.alibaba.fastjso ...

  9. malloc.c

    glibc-2.14中的malloc.c源代码,供研究malloc和free实现使用: /* Malloc implementation for multiple threads without lo ...

  10. 转载VC6.0 子窗口和父窗口

    这个是我周一在一家公司做的上机题中的一道,当场没做出来.我当时只跟考官说了设计思路,是带回来查了几本资料书之后才完成的.因为有半个学期没用VC开发了……,最近一直都在实践ASP.NET相关的…… 建立 ...