2016级算法第六次上机-F.AlvinZH的学霸养成记VI
1082 AlvinZH的学霸养成记VI
思路
难题,凸包。
分析问题,平面上给出两类点,问能否用一条直线将二者分离。
首先应该联想到这是一个凸包问题,分别计算两类点的凸包,如果存在符合题意的直线,那么这两个凸包(凸多边形)一定是不相交的。
计算凸包一般有两种方法,Graham扫描法和Jarvis步进法。
Graham扫描法比较简单,好理解,书中也有伪代码。先找到最左下点P0,对剩下的点相对P0进行极角排序。然后依次进栈判断。当算法终止时,栈中从底部到顶部,依次是按逆时针方向排列的凸包中的点(有时候需要利用这一点)。
这个方法有一个缺点是如果想求得纯粹的顶点,即不包含共线点,很容易出错。中间的点可以通过叉积直接排除,对栈底部和顶部还需要额外的判断。
bool cmp(const Point& p1,const Point& p2)
{
double C = Cross(p1-p[0], p2-p[0]);
return C ? C>0 : dis(p[0],p1) < dis(p[0],p2);
}
//点集凸包
void Graham(int n)
{
double x=p[0].x;
double y=p[0].y;
int mi=0;
for(int i=1;i<n;i++)//找到最左下点
{
if(p[i].x<x||(p[i].x==x&&p[i].y<y))
{
x=p[i].x;
y=p[i].y;
mi=i;
}
}
Point tmp=p[mi];
p[mi]=p[0];
p[0]=tmp;
sort(p+1,p+n,cmp);//极角排序
stack[0]=p[0];
stack[1]=p[1];
stack[2]=p[2];
int top=2;
for (int i=3 ; i<n ; ++i)
{
while(crossProd(stack[top-1],stack[top],p[i])<=0&&top>=2)
--top;
stack[++top]=p[i];
}
}
解题代码中采用Jarvis步进法,先把点集按先y后x排序,从最低点到最高点遍历,再从最高点到最低点遍历,即可找到凸包上所有的点。算法终止时,数组CH中也是按逆时针顺序排列的,且最后一点与第一点相同。
x、y意义上是相同的,下列代码中采用从左到右、再从右到左,排序也作相应改变,先x后y,效果一样。
bool operator < (const Point& p1, const Point& p2) {
return p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y);
}
//点集凸包
vector<Point> ConvexHull(vector<Point> p) {
//预处理,删除重复点
sort(p.begin(), p.end());
p.erase(unique(p.begin(), p.end()), p.end());
int n = p.size();
int m = 0;
vector<Point> ch(n+1);
for(int i = 0; i < n; i++) {
while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
ch[m++] = p[i];
}
int k = m;
for(int i = n-2; i >= 0; i--) {
while(m > k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
ch[m++] = p[i];
}
if(n > 1) m--;
ch.resize(m);
return ch;
}
第二个问题,判断红点和蓝点分别组成的两个凸包(凸多边形)是否相离。完全暴力判断,别无他法。
①任取一个红点,判断是否在蓝凸包内or上。如果是,则无解。蓝点红凸包同理。
二分法:判断点在凸多边形内,n个点总时间复杂度O(nlgn)。
②任取红凸包上的一条线段和蓝凸包上的一条线段,判断二者是否相交。如果相交(不一定是规范相交,有公共点就算相交),则无解。
方法:叉积的应用。如果另一线段两端点分别在这一线段的两侧,那么线段可能相交(也可能在线段外),否则不可能相交。对另一线段采用相同方法就可判断出是否相交了。
任何一个凸包退化成点或者线段时本来需要特判,上面两种情况已经包含。
分析
求解凸包的两种算法中,Graham扫描法时间复杂度\(O(nlgn),Jarvis步进法时间复杂度为\)O(nh)。
求解凸包方法:http://www.csie.ntnu.edu.tw/~u91029/ConvexHull.html(页面粉粉的
判断相离复杂度较高,分析无意义。
参考代码
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
//精度判断
const double eps = 1e-10;
double dcmp(double x) {
if(fabs(x) < eps) return 0;
else return x < 0 ? -1 : 1;
}
struct Point {
double x, y;
Point(double x=0, double y=0):x(x),y(y) {}
};
Point operator - (const Point& A, const Point& B) {
return Point(A.x-B.x, A.y-B.y);
}
double Cross(const Point& A, const Point& B) {
return A.x*B.y - A.y*B.x;
}
double Dot(const Point& A, const Point& B) {
return A.x*B.x + A.y*B.y;
}
bool operator < (const Point& p1, const Point& p2) {
return p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y);
}
bool operator == (const Point& p1, const Point& p2) {
return p1.x == p2.x && p1.y == p2.y;
}
//判断两条线段是否相离
bool SegmentProperIntersection(const Point& a1, const Point& a2, const Point& b1, const Point& b2) {
double c1 = Cross(a2-a1,b1-a1), c2 = Cross(a2-a1,b2-a1),
c3 = Cross(b2-b1,a1-b1), c4=Cross(b2-b1,a2-b1);
return dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0;
}
bool OnSegment(const Point& p, const Point& a1, const Point& a2) {
return dcmp(Cross(a1-p, a2-p)) == 0 && dcmp(Dot(a1-p, a2-p)) < 0;
}
//点集凸包,Jarvis步进法
vector<Point> ConvexHull(vector<Point> p) {
//预处理,删除重复点
sort(p.begin(), p.end());
p.erase(unique(p.begin(), p.end()), p.end());
int n = p.size();
int m = 0;
vector<Point> ch(n+1);
for(int i = 0; i < n; i++) {
while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
ch[m++] = p[i];
}
int k = m;
for(int i = n-2; i >= 0; i--) {
while(m > k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
ch[m++] = p[i];
}
if(n > 1) m--;
ch.resize(m);
return ch;
}
//判断点与凸多边形是否相离
int IsPointInPolygon(const Point& p, const vector<Point>& poly) {
int wn = 0;
int n = poly.size();
for(int i=0; i<n; ++i) {
const Point& p1 = poly[i];
const Point& p2 = poly[(i+1)%n];
if(p1 == p || p2 == p || OnSegment(p, p1, p2)) return -1;//在边界上
int k = dcmp(Cross(p2-p1, p-p1));
int d1 = dcmp(p1.y - p.y);
int d2 = dcmp(p2.y - p.y);
if(k > 0 && d1 <= 0 && d2 > 0) wn++;
if(k < 0 && d2 <= 0 && d1 > 0) wn--;
}
if(wn != 0) return 1;//内部
return 0;//外部
}
bool ConvexPolygonDisjoint(const vector<Point> ch1, const vector<Point> ch2) {
int c1 = ch1.size();
int c2 = ch2.size();
for(int i=0; i<c1; ++i)
if(IsPointInPolygon(ch1[i], ch2) != 0) return false;
for(int i=0; i<c2; ++i)
if(IsPointInPolygon(ch2[i], ch1) != 0) return false;
for(int i=0; i<c1; ++i)
for(int j=0; j<c2; ++j)
if(SegmentProperIntersection(ch1[i], ch1[(i+1)%c1], ch2[j], ch2[(j+1)%c2])) return false;
return true;
}
int main()
{
int n, m;
while(scanf("%d %d", &n, &m) == 2 && n > 0 && m > 0)
{
vector<Point> P1, P2;
double x, y;
for(int i = 0; i < n; i++) {
scanf("%lf %lf", &x, &y);
P1.push_back(Point(x, y));
}
for(int i = 0; i < m; i++) {
scanf("%lf %lf", &x, &y);
P2.push_back(Point(x, y));
}
if(ConvexPolygonDisjoint(ConvexHull(P1), ConvexHull(P2)))
printf("YES\n");
else
printf("NO\n");
}
}
2016级算法第六次上机-F.AlvinZH的学霸养成记VI的更多相关文章
- 2016级算法第六次上机-D.AlvinZH的学霸养成记V
1081 AlvinZH的学霸养成记V 思路 中等题,计算几何. 这是一个排序问题,按极角排序.可以转化为叉积的应用,对于点A和B,通过叉积可以判断角度大小,共线时再判断距离. 叉积的应用.OA × ...
- 2016级算法第六次上机-C.AlvinZH的学霸养成记II
1032 AlvinZH的学霸养成记II 思路 中等题,贪心. 所有课程按照DDL的大小来排序. 维护一个当前时间curTime,初始为0. 遍历课程,curTime加上此课程持续时间d,如果这时cu ...
- 2016级算法第五次上机-E.AlvinZH的学霸养成记IV
1039 AlvinZH的学霸养成记IV 思路 难题,最大二分图匹配. 难点在于如何转化问题,n对n,一个只能攻击一个,判断是否存在一种攻击方案我方不死团灭对方.可以想到把所有随从看作点,对于可攻击的 ...
- 2016级算法第五次上机-D.AlvinZH的学霸养成记III
850 AlvinZH的学霸养成记III 思路 难题.概率DP. 第一种思考方式:直接DP dp[i]:从已经有i个学霸到所有人变成学霸的期望. 那么答案为dp[1],需要从后往前逆推.对于某一天,有 ...
- 2016级算法第四次上机-F.AlvinZH的最“长”公共子序列
940 AlvinZH的最"长"公共子序列 思路 DP,难题. \(dp[i][j]\) :记录A的前i个字符与B的前j个字符变成相同需要的最小操作数. 初始化:dp[i][0] ...
- 2016级算法第六次上机-E.Bamboo之吃我一拳
Bamboo之吃我一拳 分析 当两个点的距离<=d时,才可以出拳,想要使得满足出拳条件的点对最少但不为0 寻找最近点对距离,得到的最近距离能够使得可以出拳的组数最少,因为除了最近点对外其他组合均 ...
- 2016级算法第六次上机-B.ModricWang's FFT : EASY VERSION
1114 ModricWang's FFT EASY VERSION 思路 利用FFT做大整数乘法,实际上是把大整数变成多项式,然后做多项式乘法. 例如,对于\(1234\),改写成\(f(x)=1* ...
- 2016级算法第六次上机-A.Bamboo之寻找小金刚
Bamboo之寻找小金刚 分析 可以抽象为许多连续线段,分别计数左拐和右拐的个数.考察叉积的基础应用. 假设ABC三点构成一个夹角∠ABC,B就是拐点,AC是辅助形成夹角.考虑线段AB和BC形成的向量 ...
- 2016级算法第五次上机-F.ModricWang的水系法术
1066 ModricWang的水系法术 思路 比较典型的最大流问题,需要注意的是,题目已经暗示(明示)了这里的边是双向的,在建图的时候需要加上反向边的容量值. 解决最大流问题的基本思路就是不断在残量 ...
随机推荐
- lua简单包装
#ifndef _LUA_WRAPPER_ #define _LUA_WRAPPER_ extern "C" { #include "lua.h" #inclu ...
- __imp___vsnprintf
unresolved external symbol __imp___vsnprintf 解决方案找到了. 在vs2015工程选项,链接器附加依赖项里面添加legacy_stdio_definitio ...
- [Training Video - 6] [File Reading] [Groovy] Reading Properties file
Reading Properties file : Properties prop = new Properties() def path = "D:\\SoapUIStudy\\appli ...
- [原创]Cef3 2623.1397 开启ppapi flash插件
最近发现WKE播放Flash或者游戏时会有很多BUG,例如视频无法播放或者是Stage3D无法使用等问题. 经过研究应该是精简版本导致的,所以决定尝试使用CEF3移植入SOUI,但是DEMO中版本有点 ...
- 15 Independent Alleles
Problem Figure 2. The probability of each outcome for the sum of the values on two rolled dice (blac ...
- oracle RAC 创库,停启库,删除库
1.创建数据库的命令dbca -silent -createDatabase -templateName General_Purpose.dbc -gdbname FPCSDB2 -sid FPCSD ...
- [label][git-commands] Several Git Commands
The process of Git commands Operation 1. git commit -m 'fist post' Windows PowerShellCopyright (C) 2 ...
- Mybatis 模糊查询 like【笔记】Could not set parameters for mapping
当使用mybatis 做模糊查询时如果这样写 会报 Could not set parameters for mapping: ParameterMapping{property='keywords' ...
- nancyfx中的静态内容文件夹
原文件 DefaultStaticContentsConventions.cs 可以根据需要自定调整,在代码里改的好处是通用.如果通过在webconfig里设置的话,在非iis环境下,可能会有问题. ...
- 学习python的第五天
4.30自我总结 一复习 1.查看数据类型 #数值10的位置 print(di(10)) #数值10的样式 print(type(10)) 2.关于变量的一些补充 a=1 b=1 c=1 #a,b,c ...