zoj2967 Colorful Rainbows(凸包 排序 栈)
来源:互联网 发布:吉力贝怪味糖 知乎 编辑:程序博客网 时间:2024/05/19 01:06
问题分析:
这道题是去年(2008)浙江省赛的题目,因此估计比较多人留意,我写本文的时候一共有90多个AC了。问题很简单,就是给出 n条斜率存在的直线,称 y坐标比较大的为比较“高”的,而一些直线比其他直线高的部分会把那些较低的部分遮盖,问从上往下看能看到多少条不同的直线(只需要看到一部分就好了)。
数据范围 n 达到了 5000,时限 1s。因此(N^2)的想法基本上可以直接淘汰。
一说本题是半平面交,但虽然问题差不多,其实跟一般的半平面交解法是有一定差异的,因为本体的所有“半平面”都是向上的。
算法介绍:
从凸包的 Graham扫描法中得到启示,我使用的是一种类似的排序/堆栈的办法来解决这个问题,下面是这个算法的描述。
如上图,有 n = 5 条直线,但是可以看见的是 4 条。因为斜率存在,因此可以用斜截式方程 y = k x + b来描述这些直线,保存浮点数对 <k, b>表示一条直线。 第一步是对这些直线进行按斜率排序,下面的图示可以表达这一过程:
下一步呢,我们需要剔除掉那些平行的直线,因为平行的直线处于上方的必然会完全盖住处于下方的,因此我们把第三条直线删掉:
然后枚举这些排好序的直线,并且用一个堆栈,存放一条射线(一条直线,以及它左端的一个 x 坐标)
初始化的时候,将第一条直线放到堆栈当中,然后开始点的 x 值取一个很小的数就可以了。
然后,每次加入一条直线,都与栈顶的直线进行相交,得到交点坐标 (x0, y0),如果得到的 x0 比栈顶的射线的 x坐标小,证明这条新加入的直线将栈顶的那条直线可见的部分(也就是射线)都遮盖住了,那么就从栈中把这条射线弹出,直到栈中剩下一条直线或者新交点的x0 比栈顶射线的 x 坐标值大为止。
然后将这个焦点的 x0 作为新的射线开头坐标,和枚举到的直线一起,压入栈中。
这样操作完之后,栈中的射线数目,就等于可以看见的直线数目。其实道理很简单,因为给出两条直线 L1 和 L2,如果 L1 的斜率< L2 的斜率,那么 L1 被 L2 遮盖的一定是后半部分,而 L2 被 L1遮盖的必然是前半部分。
因此,任举一条直线,它被所有斜率比他小的直线遮盖剩下的,一定是一条向右的射线。而它依然可见的充要条件,就是比它斜率大的直线并没有把它的剩下部分给遮盖完。这样再回想刚才的操作,就不难想明白这个算法的道理了。
下面是效率分析,在排序的时候需要一个 O(nlogn) 的操作,然后剩下的因为每条直线入栈都是一次,顶多出栈一次,因此是 O(n)的复杂度,总体复杂度为 O(nlogn),满足要求。
#include <iostream>#include <cstdio>#include <algorithm>#include <cmath>#include <stack>#define MIN 0.00001#define MAX 1<<30using namespace std;struct node{ double k,b;} line[10000];int num;double x[10000];bool cmp(node a,node b){ return a.k<b.k;}int judge(double a,double b){ int i; for(i=0; i<num; i++) { if(fabs(a-line[i].k)<MIN&&(b<=line[i].b)) return 0; if(fabs(a-line[i].k)<MIN&&(b>line[i].b)) { line[i].b=b; return 0; } } return 1;}double chaji(node l1,node l2){ return (l1.b-l2.b)/(l2.k-l1.k);}void slove(){ int i; stack<int> s; s.push(0); for(i=0; i<=num; i++) x[i]=-MAX; for(i=1; i<num; i++) { int pos=s.top(); double rex=chaji(line[pos],line[i]); while(rex<x[pos]||fabs(rex-x[pos])<=MIN) { s.pop(); pos=s.top(); rex=chaji(line[pos],line[i]); } x[i]=rex; s.push(i); } printf("%d\n",s.size());}int main(){ int i,T,n; double a,b; scanf("%d",&T); while(T--) { num=0; scanf("%d",&n); for(i=0; i<n; i++) { scanf("%lf%lf",&a,&b); if(judge(a,b)) { line[num].k=a; line[num].b=b; num++; } } sort(line,line+num,cmp); slove(); } return 0;}
- zoj2967 Colorful Rainbows(凸包 排序 栈)
- ZOJ2967-Colorful Rainbows
- Colorful Rainbows(计算几何)
- C - Colorful Rainbows 解题报告 (双向栈)
- ZOJ 2967Colorful Rainbows
- 省赛Colorful Rainbows
- C - Colorful Rainbows----(2015 summer training #4 (Qualifying))
- ZOJ 2967-C - Colorful Rainbows
- 【计算几何】ZOJ 2967 Colorful Rainbows
- zoj 2967 Colorful Rainbows (堆栈应用)
- ZOJ 2967——Colorful Rainbows
- zoj2967(堆栈应用)
- zoj 2967 Colorful Rainbows求不被覆盖的直线数(贪心)
- hihoCoder - 1103 - Colorful Lecture Note (栈~~)
- hihoCoder - 1103 - Colorful Lecture Note(栈、字符串处理)
- Colorful Tree(HDU 6035)
- Colorful Tree(逆向思维)
- Colorful Lecture Note(栈的模拟)
- iOS ARC相关
- 显示Intent与隐式Intent
- 五大常用算法之三:贪心算法
- MyEclipse无法在tomcat部署web项目的可能原因
- 创建删除DBLINK
- zoj2967 Colorful Rainbows(凸包 排序 栈)
- <Http权威指南 1,2,3>Http概述
- 五大常用算法之四:回溯法
- org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read
- ArrayAdapter
- PHP性能优化工具–xhprof安装
- ios UIButton得 几种触发方式
- 五大常用算法之五:分支限界法
- 记录我的旅程