题目链接: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. MHA环境的搭建

    MHA简介: MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,它由日本DeNA公司youshimaton(现就职于Facebook公司)开 ...

  2. C语言解析日志,存储数据到伯克利DB 2

    #编译程序 gcc -o historydb historydb.c -ldb #将2013年8月9日的用户记录写入数据库 (程序自动识别新用户入库,跳过老用户) ./historydb -f .us ...

  3. SGU 294 He's Circles

    题意:一个项链有n个珠子,每个珠子为黑色或白色.问有多少种不同的项链? 注意,n的数量十分大,因此,我们枚举i(1<=i<=n),令L=n/i,求出L的欧拉函数,则这些数和L互质,因此gc ...

  4. 《Programming WPF》翻译 第9章 6.我们进行到哪里了?

    原文:<Programming WPF>翻译 第9章 6.我们进行到哪里了? 只有当任何内嵌控件都没有提供你需要的底层行为时,你将要写一个自定义控件.当你写一个自定义控件,你将要使用到依赖 ...

  5. 微信开放JS-SDK,助力网页开发

    From:http://mp.weixin.qq.com/s?__biz=MjM5NDAwMTA2MA==&mid=209208141&idx=1&sn=1f075212b34 ...

  6. phpcms 内部检测用户名长度更改

    <?php /*1.js部分ajax就不说了,很好改...把min  max更改下就行了具体在三个地方更改: inputValidator({min:2,max:40, //默认是20 1.会员 ...

  7. phpcms加载系统类与加载应用类之区别详解

    <?php 1. 加载系统类方法load_sys_class($classname, $path = ''", $initialize = 1)系统类文件所在的文件路径:/phpcms ...

  8. jquery 自动跳出列表

    先上效果图:当鼠标经过相亲会自动弹出取最新的10条数据

  9. IOS中的各种Picker

    简述 在应用的一些设置中经常要用到一些Picker来快速帮助用户选定取值,一般会用到的有UIDatePicker,UIPickerView以及UIImagePickerController. 初始界面 ...

  10. [Immutable,js] Iterating Over an Immutable.js Map()

    Immutable.js provides several methods to iterate over an Immutable.Map(). These also apply to the ot ...