POJ 1151 Atlantis 求矩阵面积并 扫描线 具体解释
题意:
给定n个矩阵的左下角和右上角坐标,求矩阵面积并(矩阵总是正放的,即与x轴y轴都平行)
思路:
扫描线裸题
http://www.cnblogs.com/fenshen371/p/3214092.html
对于一个矩阵,我们仅仅关心他的上下底边。线段树维护的是当前有哪些区间是被线段覆盖的(即把线段都移动到x轴上,x轴被覆盖的长度是多少)
我们从上往下扫,则对于随意一条(如15,10) 我们会计算(20,13)-(15,10)之间的矩形面积
而对于(11,8),我们会计算(15,10)-(11,8)之间的矩形面积。
矩形面积= 底边长*高
1、显然这里的高就是(15,10)-(11,8) ; 更通俗的就是:line[i-1].y - line[i].y;
2、而底边长则是取决于从(11,8) 向上(不包含(11,8))有多长的区间被线段覆盖了,这个长度则是用线段树来维护。
3、而(11,8)这条线段对于线段树或者说 对区间是覆盖还是删除则取决于这条线段是上底边还是下底边(上底边则是覆盖,下底边则是删除)
对于线段树的建树也要注意一个要点:
平时建树的习惯是:
[1,4]
/ \
[1,2] [3,4]
而我们用线段树维护的是x轴坐标,是连续的区间,假设这样建树则忽略掉了区间[2,3]
所以我们建成如此:
[1,4]
/ \
[1,2] [2,4]
即: build(l,mid,L(id)); build(mid,r,R(id)); mid不+1了
推断某区间是否为最底层区间时则写成 if(tree[id].l +1 == tree[id].r) ;
由于[1,1]这个区间对我们是没有意义的,即 l < r才是有长度的区间,才是有意义的。而最小的l < r 就是 l +1 = r 的情况。
代码:
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <queue> #include <set> #include <map> using namespace std; #define N 205 #define L(x) (x<<1) #define R(x) (x<<1|1) int Mid(int a,int b){return (a+b)>>1;} //这份代码是对y轴建树,从左到右扫 struct node{ //这个区间被c条线段覆盖了 int l, r; int c; //用来记录重叠情况 double cnt, lf, rf;//离散点l相应的实际点lf cnt表示这个区间内存在的线段长度 }tree[N*4]; struct Line{ double x,y1,y2; int f; Line(double a=0.0,double b=0.0,double c=0.0, int d=0):x(a),y1(b),y2(c),f(d){} }line[N*2]; bool cmp(Line a, Line b){return a.x<b.x;} double y[N]; void build(int l, int r, int id){ tree[id].l = l, tree[id].r = r; tree[id].cnt = tree[id].c = 0; tree[id].lf = y[l]; tree[id].rf = y[r]; if(l+1==r)return ; int mid = Mid(l,r); build(l, mid, L(id)); build(mid,r,R(id)); } void push_up(int id){ if(tree[id].c){ tree[id].cnt = tree[id].rf - tree[id].lf; return ; } if(tree[id].l+1 == tree[id].r) tree[id].cnt = 0; else tree[id].cnt = tree[L(id)].cnt + tree[R(id)].cnt; } void update(int id, Line e){ if(e.y1 == tree[id].lf && tree[id].rf == e.y2){ tree[id].c += e.f; push_up(id); return ; } if(e.y2 <= tree[L(id)].rf) update(L(id),e); else if(tree[R(id)].lf <= e.y1) update(R(id),e); else { Line tmp = e; tmp.y2 = tree[L(id)].rf; update(L(id),tmp); tmp = e; tmp.y1 = tree[R(id)].lf; update(R(id),tmp); } push_up(id); } set<double>myset; set<double>::iterator p; int main(){ int i, j, n, Cas = 1; double x1,x2,y1,y2; while(scanf("%d",&n),n){ myset.clear(); int top = 1; for(i = 1;i <= n; i++){ scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2); myset.insert(y1); myset.insert(y2); Line E = Line(x1,y1,y2,1); line[top++] = E; Line E2= Line(x2,y1,y2,-1); line[top++] = E2; } sort(line+1, line+top, cmp); int siz = 1; for(p=myset.begin(); p!=myset.end(); p++) y[siz++] = *p; build(1, siz-1, 1); update(1, line[1]); double ans = 0; for(i = 2; i < top; i++){ ans += tree[1].cnt * (line[i].x - line[i-1].x); update(1, line[i]); } printf("Test case #%d\nTotal explored area: %.2lf\n\n",Cas++,ans); } return 0; }