给定n(n<=100)个矩形,
每个矩形x1,y1,x2,y2四个参数,代表左下角的点(x1,y1)和右上角的点(x2,y2)
求所有矩形的面积并
https://www.cnblogs.com/KonjakJuruo/p/6024266.html(扫描线具体流程)
https://blog.csdn.net/stay_accept/article/details/50503332(含代码注释,较为详细)
注意矩形n为100的话,至少200条边,
这样建线段树的话就需要开800的数组,不过还是开2000比较稳...
每次计算当前线段对当前不封顶的线段的面积的贡献,最后求和即可
嚷嚷着扫描线一年了,终于把扫描线入门学会了……
现在觉得,打游戏对敲代码真的有帮助鸭……
晚上自闭3h看懂了扫描线的线段和点的下标处理不同部分和扫线的部分……
估计代码可能要敲1h,然而打了一波游戏之后20min就敲出来了……
劳逸结合鸭QAQ
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=2005;
int cas,n,now;
double ans;
double x[maxn];
double x1,y1,x2,y2;
struct node
{
int l,r;
double len;//每个节点都维护一个线段长度
int cnt;//当前有cnt条重叠的下线段
}e[maxn];
struct edge
{
double l,r,h;//当前线段的左界,右界,高度
int op;//矩形op==1是下线段,-1是上线段
edge(){}
edge(double ll,double rr,double hh,int o):l(ll),r(rr),h(hh),op(o){}
friend bool operator<(edge a,edge b){return a.h0)e[p].len=x[e[p].r+1]-x[e[p].l];//[l,r)的线段由[l,r-1]表示,但长度仍为[l,r]的长度
else if(e[p].l==e[p].r)e[p].len=0;
else e[p].len=e[p<<1].len+e[p<<1|1].len;//左右子的贡献之和
}
void update(int p,int ql,int qr,int op)
{
if(ql<=e[p].l&&e[p].r<=qr)
{
e[p].cnt+=op;
pushup(p);
return;
}
int mid=(e[p].l+e[p].r)/2;
if(ql<=mid)update(p<<1,ql,qr,op);
if(qr>mid)update(p<<1|1,ql,qr,op);
pushup(p);
}
int main()
{
while(~scanf("%d",&n)&&n)
{
ans=0;cas++;
for(int i=1;i<=n;++i)//1到2*n
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
x[2*i-1]=x1;x[2*i]=x2;
g[2*i-1]=edge(x1,x2,y1,1);
g[2*i]=edge(x1,x2,y2,-1);
}
sort(x+1,x+2*n+1);
now=1;
for(int i=2;i<=2*n;++i)//强制去重x数组
if(x[i]!=x[now])x[++now]=x[i];
build(1,1,now);
sort(g+1,g+2*n+1);
for(int i=1;i<=2*n-1;++i)
{
int l=lower_bound(x+1,x+now+1,g[i].l)-x;
int r=lower_bound(x+1,x+now+1,g[i].r)-x;
//[l,l+1)代表线段l,边权线段比点少一个 3个点2条边
//[l,r)内有线段r-l条,故用[l,r-1]来代替
//每个线段对应一个标号
update(1,l,r-1,g[i].op);
ans+=(g[i+1].h-g[i].h)*e[1].len;
}
printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas,ans);
}
return 0;
}