HDU 1542 Atlantis

Posted buerdepepeqi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 1542 Atlantis相关的知识,希望对你有一定的参考价值。

Atlantis

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 17606    Accepted Submission(s): 7154


Problem Description
There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some of these texts even include maps of parts of the island. But unfortunately, these maps describe different regions of Atlantis. Your friend Bill has to know the total area for which maps exist. You (unwisely) volunteered to write a program that calculates this quantity.
 

 

Input
The input file consists of several test cases. Each test case starts with a line containing a single integer n (1<=n<=100) of available maps. The n following lines describe one map each. Each of these lines contains four numbers x1;y1;x2;y2 (0<=x1<x2<=100000;0<=y1<y2<=100000), not necessarily integers. The values (x1; y1) and (x2;y2) are the coordinates of the top-left resp. bottom-right corner of the mapped area.

The input file is terminated by a line containing a single 0. Don’t process it.
 

 

Output
For each test case, your program should output one section. The first line of each section must be “Test case #k”, where k is the number of the test case (starting with 1). The second one must be “Total explored area: a”, where a is the total explored area (i.e. the area of the union of all rectangles in this test case), printed exact to two digits to the right of the decimal point.

Output a blank line after each test case.
 

 

Sample Input
2 10 10 20 20 15 15 25 25.5 0
 

 

Sample Output
Test case #1 Total explored area: 180.00
 
1 记录好所有的矩形坐标、按照高度排序
2.离散化x坐标
3 接着扫描线从下往上扫描,每遇到一条上下边就停下来,将这条线段投影到总区间上(总区间就是整个多边形横跨的长度),
这个投影对应的其实是个插入和删除线段操作。还记得给他们赋的值1或-1吗,下边是1,扫描到下边的话相当于往总区间插入一条线段,
上边-1,扫描到上边相当于在总区间删除一条线段(如果说插入删除比较抽象,那么就直白说,扫描到下边,投影到总区间,
对应的那一段的值都要增1,扫描到上边对应的那一段的值都要减1,如果总区间某一段的值为0,说明其实没有线段覆盖到它,为正数则有
每扫描到一条上下边后并投影到总区间后,就判断总区间现在被覆盖的总长度,然后用下一条边的高度减去当前这条边的高度,
乘上总区间被覆盖的长度,就能得到一块面积,并依此做下去,就能得到最后的面积
 
代码如下
技术分享图片
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
#pragma comment(linker, "/STACK:102400000,102400000")
#define clr(x) memset(x,0,sizeof(x));
using namespace std;
const int  maxn 105+5;
struct segment//保存矩形的上下边
{
    double l,r,h;//左右横坐标,纵坐标
    int f;//1为下边界,-1为上边界
    segment() {};
    segment(double x1,double x2,double y,int ic)
    {
        l=x1,r=x2,h=y,f=ic;
    }
    bool operator < (const segment&A)const//按高度从小到大排序
    {
        return h<A.h;
    }
} p[maxn];
struct node{
    int le,ri;
    int cnt;//该节点被覆盖的情况
    double len;//该区间被覆盖的总长度
    int mid()
    {
        return (le+ri)>>1;
    }
} tree[maxn<<2];
double pos[maxn];

void Build(int rt,int le,int ri){
    tree[rt].le=le;
    tree[rt].ri=ri;
    tree[rt].len=0;
    tree[rt].cnt=0;
    if(le==ri)
        return ;
    int mid=tree[rt].mid();
    Build(rt<<1,le,mid);
    Build(rt<<1|1,mid+1,ri);
}

void Upfather(int rt){

    //计算长度的时候需要把原来的点+1,因为要算这一段的长度
   if(tree[rt].cnt)//非0,已经被整段覆盖
        //计算节点的长度 
        tree[rt].len=pos[tree[rt].ri+1]-pos[tree[rt].le];
    else if(tree[rt].le==tree[rt].ri)//已经不是一条线段
        tree[rt].len=0;
    else//是一条线段但是又没有整段覆盖,那么只能从左右孩子的信息中获取
        tree[rt].len=tree[rt<<1].len+tree[rt<<1|1].len;
}

void Update(int rt,int val,int left,int right){
    if(left<=tree[rt].le&&tree[rt].ri<=right)
    {
        tree[rt].cnt+=val;//更新这个区间被覆盖的情况
        Upfather(rt);//更新这个区间被覆盖的总长度
        return ;
    }
    int mid=tree[rt].mid();
    if(left<=mid)
        Update(rt<<1,val,left,right);
    if(right>mid)
        Update(rt<<1|1,val,left,right);
    Upfather(rt);//计算该区间被覆盖的总长度
}

int Search(int le,int ri,double k){
    while(le<=ri)
    {
        int mid=(le+ri)>>1;
        if(pos[mid]==k)
            return mid;
        else if(pos[mid]>k)
            ri=mid-1;
        else
            le=mid+1;
    }
}

int main(){
#ifndef ONLINE_JUDGE
    FIN
#endif
    int n,k=0;
    double x1,x2,y1,y2;
    while(~scanf("%d",&n),n){
        int tot=0;
        for(int i=0; i<n; ++i){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            pos[tot]=x1;
            p[tot++]=segment(x1,x2,y1,1);//记录下边界的信息
            pos[tot]=x2;
            p[tot++]=segment(x1,x2,y2,-1);//记录上边界的信息
        }
        sort(pos,pos+tot);//横坐标升序排序
        sort(p,p+tot);//按高度排序
        int m=1;
        for(int i=1; i<tot; ++i)//去重
            if(pos[i]!=pos[i-1])
                pos[m++]=pos[i];
        Build(1,0,m-1);//离散化后的区间就是[0,m-1],以此建树
        double ans=0;
        // cout<<tot<<endl;
        // cout<<m<<endl;
        for(int i=0; i<tot; ++i)//拿出每条横线并且更新
        {
            //线段树记录的是点
            //所以每个点的查询从0开始,后面每个坐标要 -1
            //树上的节点的区间
            int le=Search(0,m-1,p[i].l); //线段左边坐标
            int ri=Search(0,m-1,p[i].r)-1; //线段右边坐标
            //cout<<le<<"------"<<ri<<endl;
            Update(1,p[i].f,le,ri);
            ans+。=tree[1].len*(p[i+1].h-p[i].h);//求面积
        }
        printf("Test case #%d
Total explored area: %.2f
",++k,ans);
        puts("");
    }
    return 0;
}
View Code

 









以上是关于HDU 1542 Atlantis的主要内容,如果未能解决你的问题,请参考以下文章

HDU1542--Atlantis(扫描线)

hdu1542 Atlantis(扫描线+线段树+离散)矩形相交面积

HDU 1542 Atlantis

HDU 1542 Atlantis

HDU-1542 Atlantis 线段树+扫描线

HDU - 1542 Atlantis (扫描线+线段树)