今天和大家结束一下扫描线算法。我尽量详细的解释。
扫描线?是干什么的? 有一道很简单的例题 二维平面有n个平行于坐标轴的矩形,现在要求出这些矩形的总面积. 重叠部分只能算一次。
input :
3 (n个矩形)
1 1 4 4 (左下角与右上角)
3 2 6 3
5 1 8 4
这是正常人的求法, 要是换成计算机 该怎么求面积呢?
计算机 先把矩形数据存在一个结构体里
struct line{
/* l r是指这个矩形的平行与x轴的线 左边的x坐标 与右边 x的坐标*/
/* h值的不是矩形的高度 而是矩形平行与x轴 那条线的高度
因此一个矩形有两个 平行与x轴的线 所有数组要开2*N*/
int l,r,h,
int c;//c是指这条线属于矩形的底边还是上边 底边为1,上边为1
}lin[2*N];
然后把 这些数据按 h的大小从小到大排序
按h 从小到大排序后 得到
1 l=1 , r=4 ,h =1 ,c=1;
2 l=5 , r =8, h=1 ,c=1;
3 l=3 , r=6, h=2 ,c=1;
4 l=3 , r=6, h=3, c= -1;
5 l=1, r=4 , h=4 ,c= -1;
6 l=5, r=8, h=4 ,c=-1;
首先 我加h最小的 线 插入线段树里 也就是把 l=1 ,r=4,c=1; (l 到 r 的区间加c);
然后 在查询 线段是有哪些线段被覆盖 了 (也就是 tree[node]>0),设得到的值 为w
再将 w与 它的上条线段的高度差值 相乘 也就是 ans+=w*(lines[i+1].h-lines[i].h);
然后慢慢的就能求出面积 。自己动手写下就知道了。
代码:
#include<iostream>
#include<algorithm>
#include<vector>
#include<stdio.h>
using namespace std;
const int N=1e5+7;
int maxn=1e9+7;
int x1,x2,y1,y2;
struct line{
int l,r,h;
int c;
}lin[2*N];
struct node{
int cover,l,r;
}tree[16*N];
bool cmp(line x,line y){
return x.h<y.h;
}
vector<int>x;
#define m (l+r)/2
#define lson 2*node
#define rson 2*node+1
void build(int l,int r,int node){
tree[node].l=x[l-1];
tree[node].r=x[r-1];
tree[node].cover=0;
if(r-l==1)return;
build(l,m,lson);
build(m,r,rson);
}
void update(int v,int ql,int qr,int node){
if(ql>=tree[node].r||tree[node].l>=qr)return;
if(ql<=tree[node].l&&qr>=tree[node].r){
tree[node].cover+=v;
return;
}
if(tree[node].r-tree[node].l==1)return;
update(v,ql,qr,lson);
update(v,ql,qr,rson);
}
int query(int node){
if(tree[node].l==0&&tree[node].r==0)return 0;
if(tree[node].cover>0){
return tree[node].r-tree[node].l;
}
int ans=0;
ans+=query(lson);
ans+=query(rson);
return ans;
}
int main(){
int n,cnt=1;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
lin[cnt].l=x1,lin[cnt].r=x2,lin[cnt].h=y1,lin[cnt++].c=1;
lin[cnt].l=x1,lin[cnt].r=x2,lin[cnt].h=y2,lin[cnt++].c=-1;
x.push_back(x1);
x.push_back(x2);
}
sort(lin+1,lin+cnt,cmp);
sort(x.begin(),x.end());
x.erase(unique(x.begin(),x.end()),x.end());
build(1,x.size(),1);
long long area=0;
for(int i=1;i<cnt-1;i++){
update(lin[i].c,lin[i].l,lin[i].r,1);
int width=query(1);
area+=1ll*width*(lin[i+1].h-lin[i].h);
}
printf("%lld\n",area);
}
难点: 这里线段树建树与以前的线段树有所不同 ,是m到r建树不是 m+ 1到 r 建树,原因其实很简单 这里是不区间查询 l到r是tree【node】>0,而是查询 整棵树上tree[node]>0的区间和 ,如果还是像以前一样建树会导致两个叶子节点的值为0 ;
自己动手画下就知道了。
在来看到扫描求周长的题
其实和球面积差不多 横着扫一遍边长 竖着在扫一遍边长。就可以了 注意 这里的线不仅要按h优先还要按 c大优先 自己想一下就知道了 。
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cstdio>
const int N=5e4+7;
using namespace std;
struct line{
int l,r,h,c;
}lines[2*N];
vector<int>x;
struct node{
int c,l,r;
}tree[4*N];
struct edge{
int x1,y1,x2,y2;
}p[N];
bool cmp(line a,line b){
if(a.h==b.h)return a.c>b.c;
return a.h<b.h;
}
#define m (l+r)/2
#define lson 2*node
#define rson 2*node+1
void build(int l,int r,int node){
tree[node].l=x[l-1];
tree[node].r=x[r-1];
if(r-l==1)return;
build(l,m,lson);
build(m,r,rson);
}
void update(int v,int ql,int qr,int node){
if(tree[node].l>=qr||tree[node].r<=ql)return;
if(ql<=tree[node].l&&qr>=tree[node].r){
tree[node].c+=v;
return;
}
update(v,ql,qr,lson);
update(v,ql,qr,rson);
}
int query(int node){
if(tree[node].c)return abs(tree[node].r-tree[node].l);
if(tree[node].l==0&&tree[node].r==0)return 0;
int ans=0;
ans+=query(lson);
ans+=query(rson);
return ans;
}
int main(){
int n,cnt=1;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&p[i].x1,&p[i].y1,&p[i].x2,&p[i].y2);
lines[cnt].l=p[i].x1,lines[cnt].r=p[i].x2,lines[cnt].h=p[i].y1,lines[cnt++].c=1;
lines[cnt].l=p[i].x1,lines[cnt].r=p[i].x2,lines[cnt].h=p[i].y2,lines[cnt++].c=-1;
x.push_back(p[i].x1);x.push_back(p[i].x2);
}
sort(x.begin(),x.end());
x.erase(unique(x.begin(),x.end()),x.end());
sort(lines+1,lines+cnt,cmp);
build(1,x.size(),1);
int last=0,ans=0,now=0;
for(int i=1;i<cnt;i++){
update(lines[i].c,lines[i].l,lines[i].r,1);
now=query(1);
ans=ans+abs(now-last);
last=now;
}
cnt=1;x.clear();
for(int i=1;i<=n;i++){
lines[cnt].l=p[i].y1,lines[cnt].r=p[i].y2,lines[cnt].h=p[i].x1,lines[cnt++].c=1;
lines[cnt].l=p[i].y1,lines[cnt].r=p[i].y2,lines[cnt].h=p[i].x2,lines[cnt++].c=-1;
x.push_back(p[i].y1),x.push_back(p[i].y2);
}
memset(tree,0,sizeof(tree));
sort(x.begin(),x.end());
x.erase(unique(x.begin(),x.end()),x.end());
sort(lines+1,lines+cnt,cmp);
build(1,x.size(),1);
last=0,now=0;
for(int i=1;i<cnt;i++){
update(lines[i].c,lines[i].l,lines[i].r,1);
now=query(1);
ans=ans+abs(now-last);
last=now;
}
printf("%d\n",ans);
}