POJ 1151 Atlantis

题解在注释中,每个函数都有解释。

//
//  main.cpp
//  Richard
//
//  Created by 邵金杰 on 16/7/25.
//  Copyright © 2016年 邵金杰. All rights reserved.
//


#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
//存放纵轴的
struct line{
    double x,y1,y2;
    bool bleft;
}line[210];
double y[210];
struct node{
    int L,R;
    node *pleft,*pright;
    int cover;
    double len;
}tree[900];
int nNodetree=0;
bool operator < (const struct line &l1,const struct line &l2)
{
    return l1.x<l2.x;
}
template<class F,class T>
F bin_search(F s,F e,T val)
{
    F L=s;
    F R=e-1;
    while(L<=R)
    {
        F mid=L+(R-L)/2;
        if(!((*mid>val)||(*mid<val))) return mid;
        else if(*mid>val) R=mid-1;
        else L=mid+1;
    }
    return e;
}
int mid(node *p)
{
    return (p->L+p->R)/2;
}
//建树
void Buildtree(node *p,int L,int R)
{
    p->L=L;
    p->R=R;
    p->len=0;
    p->cover=0;
    if(L==R) return ;
    nNodetree++;
    p->pleft=tree+nNodetree;
    nNodetree++;
    p->pright=tree+nNodetree;
    Buildtree(p->pleft,L,mid(p));
    Buildtree(p->pright,mid(p)+1,R);
}
//如果区间刚好在所插入范围内,那么直接把p->len=y[R+1]-y[L],为什么要R+1,因为L和R都代表区间,所以这个区间的最上面的坐标为y[L],最下面的轴是y[R+1],相减就是矩形在该区间内所占的高,前提是完全覆盖该区间的,所以p->cover++,接下来就是区间没有完美配对的情况了,那么区间会分解,这都是套路不说了,最后那句话是更新母节点的(并非是根结点),如果该结点所代表的区间不是完全被覆盖的,那么所覆盖的矩形的高就是p->len=p->pleft->len+p->pright->len;(这句话非常关键,否则根结点无法及时更新的话,会导致该条纵轴与下一条纵轴之间的矩形面积为0,导致最后答案错误);
void Insert(node *p,int L,int R)
{
    if(p->L==L&&p->R==R)
    {
        p->len=y[R+1]-y[L];
        p->cover++;
        return ;
    }
    if(R<=mid(p)) Insert(p->pleft,L,R);
    else if(L>mid(p)) Insert(p->pright,L,R);
    else{
        Insert(p->pleft,L,mid(p));
        Insert(p->pright,mid(p)+1,R);
    }
    if(p->cover==0)
        p->len=p->pleft->len+p->pright->len;
}
//与Insert类似,如果完美配对,那么矩形之前是完美覆盖该区间的,那么现在矩形到右边的轴了,就不再覆盖了,所以p->cover--,当p->cover==0时,代表该区间已经没有完全覆盖的矩形了,如果时单个区间,那么p->len=0,如果是多个单区间组合起来的组合区间,那么p->len=p->pleft->len+p->pright->len;(因为可能有不完美覆盖该区间的矩形,但是有一部分在该区间内),接下来是区间不配对时的分解区间,最后一个if的意思也是跟新母节点,知道跟新根结点,理由在Insert函数中已经阐述过了,类似。
void Delete(node *p,int L,int R)
{
    if(p->L==L&&p->R==R)
    {
        p->cover--;
        if(p->cover==0){
            if(p->L==p->R) p->len=0;
            else p->len=p->pleft->len+p->pright->len;
        }
        return ;
    }
    if(R<=mid(p)) Delete(p->pleft,L,R);
    else if(L>mid(p)) Delete(p->pright,L,R);
    else{
        Delete(p->pleft,L,mid(p));
        Delete(p->pright,mid(p)+1,R);
    }
    if(p->cover==0)
        p->len=p->pleft->len+p->pright->len;
}
int main()
{
    int n,kase=0,yc,lc;
    double x1,x2,y1,y2;
    double area;
    while(scanf("%d",&n)&&n)
    {
        area=0;
        yc=0,lc=0;
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            y[yc++]=y1;
            y[yc++]=y2;
            line[lc].x=x1;
            line[lc].y1=y1;
            line[lc].y2=y2;
            line[lc].bleft=true;
            lc++;
            line[lc].x=x2;
            line[lc].y1=y1;
            line[lc].y2=y2;
            line[lc].bleft=false;
            lc++;
        }
        sort(y,y+yc);//整理横轴,然后分解区间
        yc=(int)(unique(y,y+yc)-y);//一共有yc条横轴
        Buildtree(tree,0,yc-1-1);//yc-1是区间数,因为区间开始是0,所以结束是yc-1-1;
        sort(line,line+lc);
        for(int i=0;i<lc-1;i++)
        {
            int L=(int)(bin_search(y,y+yc,line[i].y1)-y);
            int R=(int)(bin_search(y,y+yc,line[i].y2)-y);//寻找该矩形的区间
            if(line[i].bleft)
                Insert(tree,L,R-1);//因为找到的是线的位置,但是插入是对区间操作的,所以区间是从L~R-1的
            else
                Delete(tree,L,R-1);
            area+=tree[0].len*(line[i+1].x-line[i].x);//tree[0]代表的是在line[i].x与line[i+1].x之间举行的宽是多少,相乘之后就代表了在line[i].x~line[i+1].x之间的矩形面积,因为有lc-1个范围,所以每次area都要更新。
        }
        printf("Test case #%d\n",++kase);
        printf("Total explored area: %.2f \n",area);
        printf("\n");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值