首先说作为一位菜鸟~~虽然编了很多的代码,但是觉得遇到的经典题目不多所以提高很慢,以前听说三本很经典的书,黑书,算法导论,算法之美,于是我选择了最无语的算法导论,不是说算法导论不好,但是作为新手,算法导论就过于理论了,觉得算法导论更加适合做了大量题目之后可以看看,可以增加对算法核心理论的理解。这道题做了一个星期了,做此题的初衷是源于黑书,准备大学内把黑书看完,话说刚做的时候很痛苦,完全找不到思路,于是百度,本不想看源代码,但是实在找不到有大神说明思路,更恨自己不懂离散化和扫描线,其中最难的就是扫描线啦。、
首先线段树,线段树还是很简单的,类似于二叉搜索数,只不过是分了段而已。然后是离散化,为什么要离散化呢?百度百科解释听详细的,因为数据太多或者由于是一个连续的值,如果不离散化,空间压力太大,所以一般最简单的做法就是排序,然后去重。最后是扫描线,理解了半天才明白,首先说明我没看过扫描线的理论,可能扫描线针对不同的情况,方法是不一样的,但是要注意的是任何题目,我觉得扫描线的使用是源于题目中几何在扫描过程中有一个恒定的规律。也许规律很简单就是你找不到而已~~这次仅谈扫描线应用于求矩形周长。
扫描线思路是这样:在扫描的过程中线段树要记录几个值
区间的左右点位置:l,r
区间的总长度:len
区间被覆盖左右节点的情况:rcover,lcover
区间被覆盖的次数:cover
区间内的段数:segnum
区间所包含的线段长度:sum
要说明的是我看代码怎么也没看懂扫描线的原理,然后我是通过数据把线段树更新过程画出来之后发现的,所以下面我会把测试数据结果贴出来,测试数据就为poj的测试数据。
下面是扫描线如何运作了,扫描过程中通过insert更新cover,segnum,sum,虽难理解的是segnu,如果需要理解的话需要根据测试数据画个图就通了。
好像区间[0,10],被[1,2],[4,5]覆盖,那么num=2
好像区间[0,10],被[1,4][4,5]覆盖,那么num=1,因为他们刚好连在一起了
好像区间[0,10],被[1,5][2,6]覆盖,那么num=1,因为还是连起来的一段
所以有ST[i].segnum=ST[i*2].segnum+ST[i*2+1].segnum-ST[i*2].rcover*ST[i*2+1].lcover;
然后说一下cover,cover在扫描线太重要了!这里的处理很巧妙,因为入边和出边会是相互影响的,如果你入边没被覆盖,出边备覆盖了一点点,这怎么算呢,在扫描线理论中,把入边去掉,由于入边去掉之后,那么sum就会更新为覆盖出边的几个矩形重合部分的长度只和,于是没被覆盖的值就是变化之前的减去变化之后的值。
最后segnum反映的是整体由几个举行,segnum=n,就有n个矩形,所以计算的时候选择*2或者*4
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
#define MAXN 10010
struct STnode //线段树的节点
{
int l,r;
int len;//区间内代表的长度
int segnum;//区间内被分成的段数,不懂的话再结合代码看看
int cover;//区间被覆盖的次数
int sum;//区间中被覆盖的总长度
bool lcover,rcover;//标记左右端点是否被覆盖,用于合并区间时候统计区间内的离散线段数
STnode()//初始化
{
l = r = 0;
len = segnum = cover = sum = 0;
lcover = rcover = false;
}
};
STnode ST[MAXN*