(使用线段树实现的)扫描线算法
Posted 六花的邪王真眼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(使用线段树实现的)扫描线算法相关的知识,希望对你有一定的参考价值。
(使用线段树实现的)扫描线算法
一、算法应用场景
一个空间中存在若干矩形,且矩形的放置方向一致——(矩形的每条边必然和X或者Y轴平行)
求这些矩形覆盖的总面积的大小。(存在若干个矩形相互重叠的问题)
二、解决思路
考虑线段树可以logN的时间内做到区间覆盖,区间设置特殊值。因此应当采用线段树进行计算。
考虑使用线段树维护当前X轴的长度,则面积应当为:Σlength*hight。其中hight为当前Y轴长度减去上一个Y轴长度。length为当前维护的结果。
考虑当将矩形切割成两条形如,x1,x2,y,val的命令,其中x1,x2代表当前上线/下线的长度;y代表当前高度,val取1或-1分别带至矩形的起始边和结束边。
三、代码实现
对于当前应用场景,具有很明显的特点,及,对于任意一个矩形,都必然将初始边和结束边加进线段树中。则若某给定区间为a,b时,不存在当a,b边对应的结束边还未被加进线段树时就被清零或部分清零的可能性。
因此,对于任意区间,都有显而易见的两种状态:
1,明确的知道该区间已经被填满。——则区间长度为区间所能表示的上下限之差。
2,不知道该区间是否被填满。——则区间长度为两个子区间之差。
对于2情况有一个子状态:及当前区间仅仅包含一个元素:(a == b-1) ——我代码中区间定义为左闭右开。对于该区间,若无明确的增加指令,则应当认为区间长度为0。
#include<bits/stdc++.h> using namespace std; #define ll long long const ll MAXN= 1<<19; const ll MAXM = 2333; int t,n,sizeOfMap =0;; class Node { public: int l,r,number; double sum; double lf,rf; }; Node tree[MAXN]; class Command { public: double x1,x2,y; int val; const bool operator < (Command const &c) { return this->y < c.y; } Command(){} Command(double x1,double x2,double y,int v) { this->x1 = x1; this->x2 = x2; this->y = y; this->val = v; } };Command commands[MAXM]; int command_number = 0; void tree_init(int a,int b,int now) { tree[now].l = a; tree[now].r = b; tree[now].number = 0; tree[now].sum = 0; if(a == b-1)return; int mid = (a+b)/2; tree_init(a,mid,now*2); tree_init(mid,b,now*2+1); } double arr[MAXM]; int find_pos(double tar) { return lower_bound(arr,arr+sizeOfMap,tar)-arr; } void update(int a,int b,int now,int key) { int l = tree[now].l; int r = tree[now].r; int mid = (l+r)/2; int num = tree[now].number; int lc = now * 2; int rc = now * 2 + 1; if(l == a && r ==b) { tree[now].number += key; if(tree[now].number == 0) { if(l == r-1)tree[now].sum =0; else tree[now].sum = tree[lc].sum + tree[rc].sum; }else tree[now].sum = arr[r]-arr[l]; return ; } if(a < mid) { update(a,min(b,mid),lc,key); if(b > mid)update(mid,b,rc,key); }else update(a,b,rc,key); if(tree[now].number == 0) { if(l == r-1)tree[now].sum =0; else tree[now].sum = tree[lc].sum + tree[rc].sum; }else tree[now].sum = arr[r]-arr[l]; return ; } int cases = 1; void init() { sizeOfMap = 0; command_number = 0; // cin>>n; for(int i=0;i<n;++i) { // cin>>arr[i]; double a,b,c,d; cin>>a>>b>>c>>d; arr[sizeOfMap++] = a; arr[sizeOfMap++] = b; arr[sizeOfMap++] = c; arr[sizeOfMap++] = d; commands[command_number++]=Command(a,c,b,1); commands[command_number++]=Command(a,c,d,-1); } sort(arr,arr+sizeOfMap); sort(commands,commands+command_number); tree_init(0,sizeOfMap+2,1); double last = 0.0; double ans = 0; for(int i=0;i<command_number;++i) { double line_now = commands[i].y; if(line_now != last) { double hight = line_now - last; ans += hight * tree[1].sum; // cout<<tree[1].sum<<ends<<hight<<"last: "<<last<<ends<<line_now<<endl; last = line_now; } double x1 = commands[i].x1; double x2 = commands[i].x2; int val = commands[i].val; update(find_pos(x1),find_pos(x2),1,val); } // cout<<ans<<endl; printf("Test case #%d\nTotal explored area: %.2f\n\n",cases++,ans); } int main() { // freopen("data_scanner.in","r",stdin); cin.sync_with_stdio(false); // cin>>t; // for(int i=0;i<t;++i) while(cin>>n&&n) { init(); // cout<<tree[1].sum<<endl; } return 0; }
以上是关于(使用线段树实现的)扫描线算法的主要内容,如果未能解决你的问题,请参考以下文章