题目链接: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. python爬虫下载youtube单个视频

    __author__ = 'Sentinel'import requestsimport reimport jsonimport sysimport shutilimport urlparse &qu ...

  2. gridview动态生成列

    // 有连接的列 if (!String.IsNullOrWhiteSpace(filedModel.C_SqlDetail)) { HyperLinkField hyperColumn = new ...

  3. Android的移动存储之SharedPreferences

    在Android系统中提供了多种存储技术.通过这些存储技术可以将数据存储在各种存储介质上.比如sharedpreferences可以将数据保存着应用软件的私有存储区,这些存储区的数据只能被写入这些数据 ...

  4. BZOJ 1063 道路设计NOI2008

    http://www.lydsy.com/JudgeOnline/problem.php?id=1063 题意:给你一棵树,也有可能是不连通的,把树分成几个链,求每个点到根经过的最大链数最小,而且要输 ...

  5. 开发反模式 - SQL注入

    一.目标:编写SQL动态查询 SQL常常和程序代码一起使用.我们通常所说的SQL动态查询,是指将程序中的变量和基本SQL语句拼接成一个完整的查询语句. string sql = SELECT * FR ...

  6. Android setTextColor无效_安卓setTextColor()的参数设置方式

    通过代码setTextColor时.如果color是一个资源文件 会set失败 没有效果 遇到这样的情况有两种解决办法.亲测过.两种都是有效的 一.注解方式 通过在方法上面添加注解解决问题 代码如下 ...

  7. 转:关于rename命令ubuntu下的用法

    下面是我的遭遇:上午想批量改几个文件的名字,觉得mv在批量方面不够方便,百度到了rename这个命令,原谅我吧,我总是在百度不到结果时才去看google,以后还是少去百度的好国内很多贴子都在说linu ...

  8. 【转】git与github在ubuntu下的使用 -- 不错

    原文网址:http://www.cnblogs.com/cocowool/archive/2010/10/19/1855616.html 最近开始使用git对kohana3的文档做一些补充的工作,使用 ...

  9. 做一个有理想的IT人

    前段时间一直以来都在思考生命的价值的问题,一直在想人的一生的追求是什么.在这个物欲横流的社会,对人的价值的定义只是在财富积累的多少,这个是大多数人所认为的.但人的一生顶多百年,百年之后这些虚荣划归为尘 ...

  10. unix c 01

    gcc编译器(代码的 预处理/汇编/编译/连接) C程序员一般写程序会定义 .c和.h两种文件 .c文件(源文件)中一般放代码的实现,.h文件(头文件)中放 各种声明和定义.   gcc -E __. ...