您的位置:首页 > 其它

关于扫描线的一些理解

2015-12-27 16:46 225 查看
关于扫描线这个东西,其实是不太好开始学习的,因为百度到的东西大部分是比较高深,或者是和计算几何相关的东西。

感觉稍微写点自己的理解吧……

一、关于扫描线

关于扫描线这个东西,其实就和我们脑海中想象的是一样的,一根线在要进行的区间上进行“扫描”。

扫到的地方就在线上进行某些性质的改变。

所以扫描线更像是一种思想,而不是某些特定的算法。

二、扫描线的初级实现

拿一道例题来看。

Codeforces Educational Rouund #4 D

题意是 以Li,Ri的形式给出一些线段,让你求出恰好被覆盖了k次的区间。

我们在脑内脑补一下。

假设有一条竖直线,从左到右的把整个区间扫描了一遍。

每次遇到一个左端点,覆盖数+1,遇到一个右端点,覆盖数-1。

我们称“覆盖数的变化”为 “事件”。

在这道题里,一条线段对应的是两个事件。

我们定义一个event,记录下每条线段的L和时间的种类——即“入”或“出”。

将“入”记为+1,“出”为-1是比较容易理解的。

在这道题里我们可以发现,事件的发生顺序是由L决定的,所以我们按照L的升序将线段排序。

然后在扫描的过程中我们就可以得到 当覆盖数为k时的区间。

复杂度 O(nlogn) n为事件数(本题事件数为线段数*2)。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long long
#define db double
#define EPS 1e-15
#define inf 1e10

using namespace std;

int main(){
LL n,k,a,b,cur=0;
scanf("%I64d%I64d",&n,&k);
vector<pair<LL,LL> > event;
vector<LL> ans;
for (LL i=0;i<n;i++) {
scanf("%I64d%I64d",&a,&b);
event.push_back(make_pair(a,-1));
event.push_back(make_pair(b,1));
}
sort(event.begin(),event.end());
int cnt=0;
for (LL x=0;x<event.size();x++){
if (event[x].second==1){
if (cnt==k) ans.push_back(event[x].first);
cnt--;
}
else {
cnt++;
if (cnt==k) ans.push_back(event[x].first);
}
}
printf("%d\n",ans.size()/2);
for (int x=0;x<ans.size()/2;x++)
printf("%I64d %I64d\n", ans[x*2],ans[x*2+1]);
return 0;
}


三、扫描线的中级实现

上面说到扫描线经常可以和一些面积问题相结合,还可以使用某些数据结构来维护。

比如POJ 1151这个题就是利用了线段树来维护“事件”。

(未完待补)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: