题目来源:
POJ http://poj.org/problem?id=1177
HDU :http://acm.hdu.edu.cn/showproblem.php?pid=1255
★实在是蔡,扫描线居然硬是看了一天,就发篇博客说说叭
扫描线:
扫描线一般运用在图形处理上面,来解决一些周长 面积的问题(比如 给你很多矩形的位置,让你求他们总共的面积,注意这里的矩形可以重叠,所以问题就变得复杂了)扫描线思想因运而生?
扫描线顾名思义,就是一条直线(平行于x轴或y轴)在坐标系里面从头到尾扫描检测,具体看下面的例子
现在有2个矩形如下图,如何用扫描线求他们的周长呢?
我们可以分别用平行于x轴、y轴的直线来扫描这个图形,以平行于y轴的扫描线为例,扫描线的位置不是随便取的,而是恰好和矩形的某边重合,从左到右扫一遍如下
我们只要把每次(取1 2 3 4时)y轴上(前后有线段覆盖的区域长度差)相加,就可以得到y轴方向的周长了。这中间还会涉及到一些操作,比如把矩形的左边的线段标记为1,右边的标记为-1.
从1开始取,第一次 1覆盖了 加上1的长度 ;
到2了,这时 加上 2覆盖的区域-1覆盖的区域的长度
到3了,这时 1覆盖的区域被3抵消了(标记1 -1 的作用),并且3覆盖的区域为0
最后是4,因为3没有覆盖,直接加上4的
这就是整个过程, 另一半与此类似就不多说了,来看2道例题~
实战:
POJ 1177 Picture
思路:
这题就是解释中差不多的做法,2次扫描线,通过线段树来维护就OK了
还有要注意的是 这里不要递归到 l == r 而是 l+1 == r 因为 l == r 在这里没有意义
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=2e4+5;
const int mod=1e9+7;
typedef long long ll;
int n,m;
int sum[maxn<<2],add[maxn<<2];
struct node
{
int l,r,h,flag;
}line1[maxn<<1],line2[maxn<<1];
bool cmp(node a,node b)
{
if(a.h!=b.h) return a.h<b.h;
return a.flag>b.flag;
}
void build(int k,int l,int r)
{
sum[k]=0;
add[k]=0;
if(l+1==r) return ;
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid,r);
}
void pushup(int k,int l,int r)
{
if(add[k]){
sum[k]=r-l;
}
else if(l+1==r){
sum[k]=0;
}
else{
sum[k]=sum[k<<1]+sum[k<<1|1];
}
}
void update(int k,int l,int r,int x,int y,int flag)
{
// cout<<l<<' '<<r<<endl;
if(x<=l&&r<=y){
add[k]+=flag;
pushup(k,l,r);
return ;
}
int mid=l+r>>1;
if(mid>x) update(k<<1,l,mid,x,y,flag);
if(mid<y) update(k<<1|1,mid,r,x,y,flag);
pushup(k,l,r);
}
int main()
{
int n;
while(~scanf("%d",&n)){
int x1,y1,x2,y2;
build(1,1,20005);
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
x1+=10001; x2+=10001; //这题有负的坐标,看着怪不习惯的,所以把他们都加上10001,变成正的了
y1+=10001; y2+=10001;
line1[i].l=x1; line1[i].r=x2;
line1[i].h=y1; line1[i].flag=1;
line1[i+n].l=x1; line1[i+n].r=x2;
line1[i+n].h=y2; line1[i+n].flag=-1;
line2[i].l=y1; line2[i].r=y2;
line2[i].h=x1; line2[i].flag=1;
line2[i+n].l=y1; line2[i+n].r=y2;
line2[i+n].h=x2; line2[i+n].flag=-1;
}
sort(line1+1,line1+n*2+1,cmp);
sort(line2+1,line2+n*2+1,cmp);
int ans=0;
for(int i=1;i<=2*n;i++){
int temp=sum[1];
update(1,1,20005,line1[i].l,line1[i].r,line1[i].flag);
ans+=abs(sum[1]-temp);
// cout<<line1[i].l<<' '<<line1[i].r<<' '<<line1[i].h<<' '<<ans<<endl;
}
build(1,1,20005);
for(int i=1;i<=2*n;i++){
int temp=sum[1];
update(1,1,20005,line2[i].l,line2[i].r,line2[i].flag);
ans+=abs(sum[1]-temp);
// cout<<ans<<endl;
}
cout<<ans<<endl;
}
return 0;
}
// 1 20 - 4 9
// 1 10 4
HDU 1255 覆盖的面积
借鉴自 https://blog.csdn.net/w326159487/article/details/77714912
思路:
首先这题是有小数的,第一步就是离散化,通过STL库里面的unique函数来进行
然后求覆盖的面积,我们想一想,假如只有2个矩形,他们之间有公共的面积的话
还是用上面的图看
循环到第二条边时,1的区域有了2次覆盖,下一条边的x减去这一条边的x 再乘上这个覆盖区域的长度 就等于覆盖的面积了。我们考虑多个的时候,也可以用这种方法,因为如果有公共面积,一定会出现2次覆盖 以及后面会出现这个存在覆盖区域的矩形的右边。
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=2e3+5;
const int mod=1e9+7;
typedef long long ll;
int n,m;
double sum[maxn<<3],lvl[maxn<<1];
int cover[maxn<<3];
struct node
{
double x,y1,y2;
int in; //记录 左右边的标记
node(double a,double b,double c,int d) {x = a; y1 = b; y2 = c; in = d;} //类似于类的构造函数
node(){}
}f[maxn];
bool cmp(node a,node b){return a.x<b.x;}
int getval(double val) {return lower_bound(lvl+1,lvl+m+1,val)-lvl;} //二分查找val的位置
void update(int k,int cc,int l,int r,int x,int y)
{
if(l+1==r){
cover[k]+=cc;
sum[k]=cover[k]>1?lvl[r]-lvl[l]:0;
return ;
}
int mid=l+r>>1;
if(mid>x) update(k<<1,cc,l,mid,x,y);
if(mid<y) update(k<<1|1,cc,mid,r,x,y);
sum[k]=sum[k<<1]+sum[k<<1|1];
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
memset(sum,0,sizeof sum);
memset(cover,0,sizeof cover);
scanf("%d",&n);
for(int i=1;i<=n;i++){
double a,b,c,d;
scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
lvl[i*2-1]=b; lvl[i*2]=d;
f[i*2-1]=node(a,b,d,1);
f[i*2]=node(c,b,d,-1);
}
sort(f+1,f+2*n+1,cmp);
sort(lvl+1,lvl+2*n+1);
m=unique(lvl+1,lvl+2*n+1)-lvl-1; //去重
double ans=0;
// cout<<m<<endl;
update(1,f[1].in,1,m,getval(f[1].y1),getval(f[1].y2)); //第一次要预先处理,第一次不可能存在重复区域
for(int i=2;i<=2*n;i++){
ans+=(f[i].x-f[i-1].x)*sum[1];
// cout<<sum[1]<<endl;
update(1,f[i].in,1,m,getval(f[i].y1),getval(f[i].y2));
}
printf("%.2f\n",ans);
}
}