POJ 1177 & HDU 1828 Picture(扫描线 + 求周长)

题目链接:

POJ:http://poj.org/problem?id=1177

HDU:http://acm.hdu.edu.cn/showproblem.php?pid=1828

几个不错的讲解:

http://www.cnblogs.com/scau20110726/archive/2013/04/13/3018702.html

http://blog.csdn.net/xingyeyongheng/article/details/8931410

Description

A number of rectangular posters, photographs and other pictures of the same shape are pasted on a wall. Their sides are all vertical or horizontal. Each rectangle can be partially or totally covered by the others. The length of the boundary of the union of
all rectangles is called the perimeter.

Write a program to calculate the perimeter. An example with 7 rectangles is shown in Figure 1.

The corresponding boundary is the whole set of line segments drawn in Figure 2.

The vertices of all rectangles have integer coordinates.

Input

Your program is to read from standard input. The first line contains the number of rectangles pasted on the wall. In each of the subsequent lines, one can find the integer coordinates of the lower left vertex and the upper right vertex of each rectangle. The
values of those coordinates are given as ordered pairs consisting of an x-coordinate followed by a y-coordinate.

0 <= number of rectangles < 5000

All coordinates are in the range [-10000,10000] and any existing rectangle has a positive area.

Output

Your program is to write to standard output. The output must contain a single line with a non-negative integer which corresponds to the perimeter for the input rectangles.

Sample Input

7
-15 0 5 10
-5 8 20 25
15 -4 24 14
0 -6 16 4
2 15 10 22
30 10 36 20
34 0 40 16

Sample Output

228

Source

IOI 1998

题目链接:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
#define N 5017
#define MAX 10100
#define ll root*2
#define rr root*2+1
//#define mid (a[root].l+a[root].r)>>1

struct Line
{
    int l, r;
    int h;//高度
    int flag;//若该扫描线属于矩形的下边的横边,则叫做入边,值为1,若属于矩形的上边的横边,则叫做出边,值为-1
} line[N*2];

struct node
{
    int l, r;
    int lp, rp;//是一个标记量,只有0,1,表示这个节点左右两个端点没有被覆盖,有为1,无为0
    int num;//这个区间被多少条线段覆盖
    int cover;//表示某x轴坐标区间内是否有边覆盖
    int len;//区间覆盖边长度
} a[2*MAX*4];

bool cmp(Line a,Line b)
{
    return a.h < b.h;
}

void build(int left,int right,int root)//构造线段树
{
    a[root].l = left;
    a[root].r = right;
    a[root].cover = 0;
    a[root].len = 0;
    a[root].lp = a[root].rp = a[root].num=0;
    if(left == right)
        return;
    int mid = (a[root].l+a[root].r)>>1;
    build(left,mid,ll);
    build(mid+1,right,rr);
}
void pushup(int root)
{
    if(a[root].cover)
    {
        a[root].len = a[root].r-a[root].l+1;
        a[root].lp = a[root].rp = 1;
        a[root].num = 1;
    }
    else if(a[root].l == a[root].r)//叶子
    {
        a[root].len = 0;
        a[root].lp = a[root].rp = 0;
        a[root].num = 0;
    }
    else
    {
        a[root].len = a[ll].len+a[rr].len;
        a[root].lp = a[ll].lp;
        a[root].rp = a[rr].rp;
        a[root].num = a[ll].num + a[rr].num - (a[ll].rp&a[rr].lp);
    }
}

void update(int left,int right,int val,int root)//加入线段e,后更新线段树
{
    if(left > right)
        return;
    if(a[root].l==left && a[root].r==right)//目标区间
    {
        a[root].cover+=val;//插入或删除操作直接让cover[]+=flag。当cover[]>0时,该区间一定有边覆盖。
        pushup(root);
        return;
    }
    int mid = (a[root].l+a[root].r)>>1;
    if(right <= mid)
        update(left,right,val,ll);
    else if(left > mid)
        update(left,right,val,rr);
    else
    {
        update(left,mid,val,ll);
        update(mid+1,right,val,rr);
    }
    pushup(root);
}
int main()
{
    int n, i, k;
    while(scanf("%d",&n)!=EOF)
    {
        if(n == 0)
        {
            printf("0\n");
            continue;
        }
        k = 0;
        int x1, y1, x2, y2;
        int maxx = -MAX;//方便此题建树
        int minn = MAX;
        for(i = 0; i < n; i++)
        {
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            //本题采用平行x轴的扫描线从下往上扫描
            maxx = max(maxx, x2);
            minn = min(minn, x1);
            line[k].l = x1;
            line[k].r = x2;
            line[k].h = y1;
            line[k].flag = 1;
            line[k+1].l = x1;
            line[k+1].r = x2;
            line[k+1].h = y2;
            line[k+1].flag = -1;    //把图中的边用line存起来以便排序
            k += 2;
        }
        sort(line,line+k,cmp);
        build(minn, maxx-1, 1);

        int ans = 0;
        int prelen = 0;
        line[k] = line[k+1];//每次处理循环的最后一次
        for(i = 0; i < k; i++)
        {
            update(line[i].l,line[i].r-1,line[i].flag,1);//每插入一次就算一次 ,相对应的边在线段树中会抵消
            //ans+=(line[i+1].h-line[i].h)*a[1].len;//高 X 低
            ans += abs(a[1].len - prelen); //计算横线部分
            ans += (line[i+1].h-line[i].h) * (2*a[1].num); //计算竖线部分
            prelen = a[1].len;//之前的长度
        }
        printf("%d\n",ans);
    }
    return 0;
}
时间: 2024-10-12 18:06:16

POJ 1177 & HDU 1828 Picture(扫描线 + 求周长)的相关文章

POJ 1177/HDU 1828 picture 线段树+离散化+扫描线 轮廓周长计算

求n个图矩形放下来,有的重合有些重合一部分有些没重合,求最后总的不规则图型的轮廓长度. 我的做法是对x进行一遍扫描线,再对y做一遍同样的扫描线,相加即可.因为最后的轮廓必定是由不重合的线段长度组成的,这样理论上是对的 要注意处理高度相同的线段,把底边优先处理(在代码里就是f标记为1的线段),因为若是一个矩形的底边和另一个矩形的上边重合,则这个轮廓肯定不能算 不过POJ和HDU的数据好像都比较弱,我没进行上面的细节处理也AC了,不过一个很简单的数据就会不对,所以还是要处理一下才是真正正确的代码 我

POJ 1177 Picture(扫描线求周长)

与求面积并的差不多,但是这个与扫描的方向相同的情况不太好处理,如果扫描线离散化两次扫两遍其实也可以解决这个问题,但是这样无论在时间还是空间上稍微就有点浪费了啊.这里因为我是离散x坐标的所以对于平行于y轴的方向上的统计比较难统计.处理的方法是:标记区间左边的断点,和右边的断点,求出这个区间一共有多少个断点.就可以统计出平行于y轴的长度了.这里合并的时候需要判断右边的左区间和左边的右区间是否相同,如果相同的话,说明他们连接到了一起,要减去多加的. Picture Time Limit: 2000MS

HDU 1828 Picture(矩形周长并)

HDU 1828 Picture 题目链接 题意:给定n个矩形,输出矩形周长并 思路:利用线段树去维护,分别从4个方向扫一次,每次多一段的时候,就查询该段未被覆盖的区间长度,然后周长就加上这个长度,4个方向都加完就是答案 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 5005; int n; struct Rec { int

Picture POJ - 1177 线段树+离散化+扫描线 求交叉图像周长

参考  https://www.cnblogs.com/null00/archive/2012/04/22/2464876.html #include <stdio.h> #include <algorithm> #define LEN 10000 using namespace std; struct Node { int left; int right; int count;//被覆盖次数 //所包含的区间数量,如三条[1,2],[2,3],[4,5]线段被覆盖,则line=2

51nod 1206 &amp;&amp; hdu 1828 Picture (扫描线+离散化+线段树 矩阵周长并)

1206 Picture  题目来源: IOI 1998 基准时间限制:2 秒 空间限制:131072 KB 分值: 160 难度:6级算法题  收藏  关注 给出平面上的N个矩形(矩形的边平行于X轴和Y轴),求这些矩形组成的所有多边形的周长之和. 例如:N = 7.(矩形会有重叠的地方). 合并后的多边形: 多边形的周长包括里面未覆盖部分方块的周长. Input 第1行:1个数N.(2 <= N <= 50000) 第2 - N + 1行,每行4个数,中间用空格分隔,分别表示矩形左下和右上端

hdu 1828 Picture(线段树&amp;扫描线&amp;周长并)

Picture Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2578    Accepted Submission(s): 1363 Problem Description A number of rectangular posters, photographs and other pictures of the same shap

poj 1177 Picture(扫描线+矩形周长并)

http://poj.org/problem?id=1177 求矩形的周长并,明确的一点是对于覆盖的边的长度忽略不计. 与求面积并类似,首先离散化,对矩形的每条横边从下往上扫描.扫描过程中要完成三个任务,更新相应的区间信息,求横边长,求竖边长. 节点信息: l,r:左右区间编号 cnt:表示该区间是否被完全覆盖.cnt > 0 表示完全覆盖,否则不完全覆盖. lp,rp:表示该区间的两个端点是否被覆盖,为1被覆盖,为0没被覆盖. num:该区间被覆盖的线段数目.例如区间[1,10],当更新[2,

HDU 1828 Picture 线段树+扫描线

题意:给你一些矩形的左上角点的坐标和右下角点的坐标,求周长并 最显而易见的思路就是对于x轴和y轴做两次扫描线,对于负数的坐标进行离散化.每次增加的值是线段变化量的绝对值.具体写法和求面积并的差不多. #include <cstdio> #include <algorithm> #include <cstring> #include <vector> using namespace std; #define lson rt << 1 , l , m

HDU 1828 Picture(长方形的周长和)

HDU 1828 Picture 题目链接 题意:给定n个矩形,输出矩形周长并 思路:利用线段树去维护,分别从4个方向扫一次,每次多一段的时候,就查询该段未被覆盖的区间长度,然后周长就加上这个长度,4个方向都加完就是答案 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 5005; int n; struct Rec { int