题目链接:http://poj.org/problem?id=1177

分析:这道题主要用到了线段树、扫描线以及离散化的相关算法。

离散化

离散化是当数字不多但是范围很大时采用的一种方法,将大区间的数字映射到一个小区间上,如,有一组数字:132398,12781,2342876,232,将其离散化后将得到2,1,3,0.

离散化的算法:

 //y为大小为n的数组,存放着离散化前的数
sort(y,y+n);
int unique_count=unique(y,y+n)-y;
//find_i为自己编写的根据N来寻找对应的i的函数
find_i(N);

将不同的y值离散化后建立线段树,下面为树的节点,其中,left,right为离散化的区间值,左闭右开,count为该区间被覆盖的次数,初始化为0,inter为竖直的矩形边的区间数,之后计算平行于x轴的边的周长会用到,lflag、rflag为左右端点是否被覆盖,用于计算inter,len为被覆盖长度。

struct Node{
int left, right;
int count;
int inter;
int lflag, rflag;
int len;
};

扫描线

将垂直于x轴的矩形的边作为扫描线,对其按x大小排序后逐个进行扫描,flag为0表示右边的边,flag为1表示左边的边。

struct Scan{
int x;
int y1, y2;
int flag;
};

代码如下:

 #include <iostream>
#include <algorithm>
#include <cmath> #define MAX 10000 using namespace std; struct Scan{
int x;
int y1, y2;
int flag;
};
struct Node{
int left, right;
int count;//被覆盖次数
int inter;////覆盖后的区间数量,2*line*| |
int lflag, rflag;//左右端点是否被覆盖
int len;//测度,覆盖区间的长度
}; struct Node node[ * MAX];
struct Scan scanline[ * MAX];
int y[MAX]; bool cmp(struct Scan line1, struct Scan line2);
void build(int L, int R, int i);
void insert(int L, int R, int i);
void remove(int L, int R, int i);
void update_len(int i);
void update_inter(int i); int main(){
int N;
//freopen("picture_in.txt", "r", stdin);
cin >> N;
int x1, x2, y1, y2;
int len_now = , inter_now = ;
int total_perimeter = ;
int n = ;
while (N--){
cin >> x1 >> y1 >> x2 >> y2;
y[n] = y1;
scanline[n].x = x1;
scanline[n].y1 = y1;
scanline[n].y2 = y2;
scanline[n++].flag = ;
y[n] = y2;
scanline[n].x = x2;
scanline[n].y1 = y1;
scanline[n].y2 = y2;
scanline[n++].flag = ;
}
sort(y, y + n);
sort(scanline, scanline + n, cmp);
//y数组中不重复的个数
int unique_count = unique(y, y + n) - y;
build(, unique_count - , );
for (int i = ; i<n; i++){
if (scanline[i].flag)
//左边插入
insert(scanline[i].y1, scanline[i].y2, );
//右边消除
else
remove(scanline[i].y1, scanline[i].y2, );
if (i>){
total_perimeter += * (scanline[i].x - scanline[i - ].x)*inter_now;
}
total_perimeter += abs(len_now - node[].len);
//cout << "perimeter after y:" << total_perimeter << endl;
len_now = node[].len;
inter_now = node[].inter;
//cout << "len_now:" << len_now << " inter_now:" << inter_now << endl << endl;
}
cout << total_perimeter << endl;
return ;
} bool cmp(struct Scan line1, struct Scan line2){
if (line1.x == line2.x)
return line1.flag>line2.flag;
return line1.x<line2.x;
} //创建指定区间的线段树并初始化
void build(int L, int R, int i){
node[i].left = L;
node[i].right = R;
node[i].count = ;
node[i].inter = ;
node[i].len = ;
node[i].lflag = node[i].rflag = ;
if (R - L>){
int M = (L + R) / ;
build(L, M, * i + );
build(M, R, * i + );
}
} //左边的边出现,插入记录
//不管是插入还是消除,都一直更新到根结点,即node[0]
void insert(int L, int R, int i){
if (L <= y[node[i].left] && R >= y[node[i].right])
node[i].count++;
else if (node[i].right - node[i].left == )
return;
else{
int mid = (node[i].left + node[i].right) / ;
if (R <= y[mid])
insert(L, R, * i + );
else if (L >= y[mid])
insert(L, R, * i + );
else{
insert(L, y[mid], * i + );
insert(y[mid], R, * i + );
}
}
update_len(i);
update_inter(i);
} //右边的边出现,则消除记录
//其实就是一步步退回去
void remove(int L, int R, int i){
if (L <= y[node[i].left] && R >= y[node[i].right])
node[i].count--;
else if (node[i].right - node[i].left == )
return;
else{
int mid = (node[i].left + node[i].right) / ;
if (R <= y[mid])
remove(L, R, * i + );
else if (L >= y[mid])
remove(L, R, * i + );
else{
remove(L, y[mid], * i + );
remove(y[mid], R, * i + );
}
}
update_len(i);
update_inter(i);
} void update_len(int i){
if (node[i].count>)
node[i].len = y[node[i].right] - y[node[i].left];
else if (node[i].right - node[i].left == )
node[i].len = ;
else
node[i].len = node[ * i + ].len + node[ * i + ].len;
} void update_inter(int i){
if (node[i].count>){
node[i].lflag = ;
node[i].rflag = ;
node[i].inter = ;
}
else if (node[i].right - node[i].left == ){
node[i].lflag = ;
node[i].rflag = ;
node[i].inter = ;
}
else{
node[i].lflag = node[ * i + ].lflag;
node[i].rflag = node[ * i + ].rflag;
node[i].inter = node[ * i + ].inter + node[ * i + ].inter -
node[ * i + ].rflag*node[ * i + ].lflag;
}
}

给几组测试数据:
测试数据1:

输入:

47
-1105 -1155 -930 -285
-765 -1115 -615 -375
-705 -480 -165 -285
-705 -1200 -175 -1025
-275 -1105 -105 -385
-10 -1165 185 -285
315 -1160 400 -710
340 -1195 655 -1070
580 -1140 655 -265
325 -480 395 -335
365 -390 620 -265
365 -770 610 -665
815 -1195 1110 -1070
825 -760 1100 -660
810 -405 1115 -275
780 -700 860 -360
1065 -695 1130 -360
775 -1110 860 -735
1070 -1110 1145 -730
-1065 -95 140 260
-725 80 750 460
135 -135 490 840
135 -135 490 750
-520 40 -210 945
-595 620 215 695
670 -5 855 610
550 -75 830 -25
815 240 1085 370
980 -90 1125 145
280 150 490 315
-1035 -155 -845 -90
855 815 950 1030
785 980 860 1165
945 985 1015 1160
730 835 1075 895
875 695 935 790
-1165 420 -520 650
-1090 815 -210 945
-130 800 65 1160
120 980 690 1150
-1140 995 -125 1180
-825 1050 -195 1135
-90 865 10 1090
280 1045 625 1090
-655 1065 -245 1115
-1155 70 -790 315
-1005 110 -825 225

输出:3700

测试数据2:

输入:

-10 -10 0 10

0 -10 10 10

输出:

代码是参考另一位大牛写的,原文请见:http://www.cnblogs.com/shuaiwhu/archive/2012/04/22/2464876.html,讲解非常详细

poj 1177 picture的更多相关文章

  1. POJ 1177 Picture(线段树:扫描线求轮廓周长)

    题目链接:http://poj.org/problem?id=1177 题目大意:若干个矩形,求这些矩形重叠形成的图形的轮廓周长. 解题思路:这里引用一下大牛的思路:kuangbin 总体思路: 1. ...

  2. POJ 1177 Picture(线段树 扫描线 离散化 求矩形并面积)

    题目原网址:http://poj.org/problem?id=1177 题目中文翻译: 解题思路: 总体思路: 1.沿X轴离散化建树 2.按Y值从小到大排序平行与X轴的边,然后顺序处理 如果遇到矩形 ...

  3. poj 1177 Picture(线段树周长并)

    题目链接:http://poj.org/problem?id=1177 题意:给你n个矩形问你重叠后外边缘总共多长. 周长并与面积并很像只不过是处理的时候是   增加的周长=abs(上一次的线段的长度 ...

  4. HDU 1828 / POJ 1177 Picture (线段树扫描线,求矩阵并的周长,经典题)

    做这道题之前,建议先做POJ 1151  Atlantis,经典的扫描线求矩阵的面积并 参考连接: http://www.cnblogs.com/scau20110726/archive/2013/0 ...

  5. HDU 1828 POJ 1177 Picture

    矩形周长并 POJ上C++,G++都能过,HDU上C++过了,G++WA ,不知道为什么 #include<cstdio> #include<cstring> #include ...

  6. HDU 1828 / POJ 1177 Picture --线段树求矩形周长并

    题意:给n个矩形,求矩形周长并 解法:跟求矩形面积并差不多,不过线段树节点记录的为: len: 此区间线段长度 cover: 此区间是否被整个覆盖 lmark,rmark: 此区间左右端点是否被覆盖 ...

  7. POJ 1177 Picture(求周长并)

    题目链接 看的HH的题解..周长有两部分组成,横着和竖着的,横着通过,sum[1] - last来计算,竖着的通过标记,记录有多少段. #include <cstdio> #include ...

  8. poj 1177 Picture (线段树 扫描线 离散化 矩形周长并)

    题目链接 题意:给出n个矩形,每个矩形给左下 和 右上的坐标,求围成的周长的长度. 分析: 首先感谢大神的博客,最近做题经常看大神的博客:http://www.cnblogs.com/kuangbin ...

  9. poj 1177 --- Picture(线段树+扫描线 求矩形并的周长)

    题目链接 Description A number of rectangular posters, photographs and other pictures of the same shape a ...

随机推荐

  1. DESTOON伪静态的设置/news/1.html格式

    在本地测试了,DT默认伪静态格式是这样http://127.0.0.2/news/show/1.htmlhttp://127.0.0.2/news/show1-1.html但是这种不利于seo优化所以 ...

  2. 列表:一个打了激素的数组2 - 零基础入门学习Python011

    列表:一个打了激素的数组2 让编程改变世界 Change the world by program 从列表中获取元素 跟数组一样,我们可以通过元素的索引值(index)从列表获取单个元素,注意,列表索 ...

  3. NSDate简单的使用

    NSDateFormatter *dateFormtter=[[NSDateFormatter alloc] init]; [dateFormtter setDateFormat:@"yyy ...

  4. secure_file_priv 配置项对数据导入导出的影响

    secure_file_priv  mysqld 用这个配置项来完成对数据导入导出的限制. 例如我们可以通过 select * from tempdb.t into outfile '/home/my ...

  5. 使用 JUnit 进行单元测试 - 教程

    tanyuanji@126.com 版本历史 JUnit 该教程主要讲解 JUnit 4.x 版本的使用,以及如何在Eclipse IDE 中如何使用JUnit   目录 tanyuanji@126. ...

  6. cf A. Vasily the Bear and Triangle

    http://codeforces.com/contest/336/problem/A #include <cstdio> #include <cstring> #includ ...

  7. POJ 2653 Pick-up sticks(线段相交)

    题意:给定n个木棍依次放下,要求最终判断没被覆盖的木棍是哪些. 思路:快速排斥以及跨立实验可以判断线段相交. #include<algorithm> #include<cstdio& ...

  8. keil 中用函数指针调用函数的参数限制

    NSIC中,通过函数指针调用的函数的参数的个数没有限制,但是KeilC对此有限制,至多3个参数.因为,KeilC编译时,无法通过函数指针找到该函数的局部数据段,也就无法通过局部数据段传递参数,只能通过 ...

  9. Visual studio 使用正则表达查找替换

    原文 http://www.cnblogs.com/shineqiujuan/archive/2012/07/04/2575535.html 正则表达式是查找和替换文本模式的一种简洁而灵活的表示法.  ...

  10. smartassembly 使用指南

    原文 http://www.cnblogs.com/hsapphire/archive/2010/09/21/1832758.html smartassembly 提供了一种用于优化和混淆你的 .ne ...