Ahead
10.6.2018
开始第二个算法了
篇章1
前面就不多写了第一篇里面的有些代码后面还用到不重写了
Beginning
算法2 (EE)
概念
极边(Extremity Edge): 也就是两边为相邻极点的边(相邻很重要!!! ) (EE)
非极边 (Non-Extremity Edge):剩下的
分析
和之前的一样
对于每一条极边,也有这样的特性,所有的点都落在同侧同样也是充要的。
这个证明和之前的差不多,想一下也没什么问题的(严谨的我也不会)
所以问题就变成了找极边
伪代码
枚举边
枚举点
如果所有点落在边的一侧
它是EE
可以看出的是时间复杂度降为O(n^3)
代码
void checkEdge(Point s[],int n,int p,int q)
{
bool LEmpty = true , REmpty = true;
for(int i=1;i<=n && (LEmpty || REmpty);++i)
{
if(i!=p && i!=q)
ToLeft(S[p],S[q],S[k])? LEmpty=false : REmpty = false;
}
if(LEmpty || REmpty)
S[p].extreme = S[q].extreme = true;
}
void markEE (Point S[],int n)
{
for(int i=1;i<=n;++i) S[i].extreme = false;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(i!=j)
checkEdge(S,n,i,j);
}
算法3 (IC)
前面两个算法还是很慢的
于是又开始又花了
参考插入排序,出现了这样一个算法
如果对于一个已经完成的凸包,会有这样几种情况
所以对于新出现的点,如果它在里面,直接忽略,如果在外面,那么这个点一定是当前的合法EP,同时对原来的进行凸包进行适当调整
判定
但是,我们首要的任务是判定是否在凸包内= =
于是我们引入一个测试 In-Convex-Polygon Text
对于找点,似乎我们可以二分查找!!!!(这里就直接引视频中的图了)
对于凸包上的点排序,每次找到mid点,连接原点与mid,然后ToLeft一次判定点在那边,然后缩小范围,最后形成最后一个三角形是,就可以判断在内还是在外了
那么时间复杂度似乎到了nlogn??
但是令人失望的是,似乎并不行,类比插排,插排的二分查找缺陷在于动态变换的区间加点不容易 ,如果用vector的话上界也会到n,而凸包面临的也是如此
所以我们就直接暴力吧,和插排一样。那么如何暴力?
对于前面两种算法,可以再找左边? ?
所以,我们就直接CCW方向遍历边,然后做ToLeft 这个点在所有边的左侧,那么,显然在内部,否则在外部。 那么测试时间为n
总时间复杂度降为O(n^2);
添加
那么判定完后,要干的事就是添加了,一个很直观的想法,类似于黑夜中开灯
虚线部分就是要删除的实线形成了新的凸包。
可以发现的是,这似乎是两个临界的s和t向x连边拓展,去掉ts,保留st
所以现在的任务就是寻找s和t
这里的s和t称之为切线(Tangent)或者支持线(Suppor-Lines)
寻找
那么继续观察,除去s,t 剩下的点v与x的连线,对于v的前驱和后记,必然属于两个模式(pattern)即一个在左一个在右
那么出现以下情况
- 如果状态是LL 那么是s
- 如果状态是RR 那么是t
- 如果状态是RL 那么它位于st上,是保留点
如果状态是LR 那么它位于ts上,是删除点
而对于LR的判定只需要两次ToLeft
更近一步我们可以发现In-Convex-Polygon Text 似乎也可以那么处理
如果在内部,则不存在t与s伪代码
遍历凸包上的点v 记录ToLeft(x,v,pre[v]) 与ToLeft(x,v,nxt[v]) 如果状态是LL 那么是s 如果状态是RR 那么是t 如果状态是RL 那么它位于st上 如果状态是LR 那么它位于ts上 如果出现过s与t扩展删除 否则 返回
代码
void InConvexPolygon Text(Point S[],Point x)
{
bool flag=0;
for(int i=1;i<=n;++i)
if(S[i].extreme)
{
bool P[i] = ToLeft(x,i,S.pre[i]);
bool N[i] = ToLeft(x , i , S.nxt[i]);
if(P[i]==N[i]) flag=1;
}
if(!flag) return ;
else
{
S[x].extreme=1;
for(int i=1;i<=n;++i)
if(S[i].extreme)
{
if(P[i]==0 && N[i]==0) S[i].nxt=x,S[x].pre=i;
else if(P[i]==1 && N[i]==0) S[i].extreme = 0;
else if(P[i]==1 && N[i]==1) S[i].pre=x,S[x].nxt=i;
}
}
}
于是就形成了O(n^2)的算法
算法4(GW)
这个算法时间复杂度为O(n^2) 但是在绝大多数情况下达不到,最优化到达O(n);
对比选择排序和EE算法
我们可否减少每次查找边的范围 ?
答案是显然的
首先,构成的凸包肯定是环状的结构。那么,对于我们当前的还未完成的凸包,一定是从两个端点出发寻找下一条极边 直到形成环路
拓展
那么对于下一个点s有什么特点? 再次对比前面---ToLeft
所以,ks 一定满足所有点位于L
那么算法就很明显的 对于当前的端点,枚举所有点做ToLeft更新
然后将k更新为s
那么第一个点怎么找,方便起见,引入一个概念
lowest-then-leftmost 点(LTL点) 即最下最左点
以y坐标为第一关键字最小,x坐标为第二关键字最小
对于第二个极点选排名第二的LTL
伪代码
找到LTL
i=LTL;
找出第一条的EE
循环
遍历点
如果新点在选定点的右边,更新
i更新为k k更新为新点
直到形成环
时间复杂度上,显然遍历次数是环的大小,所以是O(nh),h为环的大小
那么当h只有3时,结果就到了O(n) O(答案?? )【据说这叫Output Sensitive】
那么这章也就先到这里了 Gw的代码网上很多不想写了,有时间补上去吧