[2019 美团春招实习笔试] 2. 染色格子数量

时间限制:C/C++语言 1000MS;其他语言 3000MS
内存限制:C/C++语言 65536KB;其他语言 589824KB
题目描述:
在二维平面上有一个无限的网格图形,初始状态下,所有的格子都是空白的。现在有n个操作,每个操作是选择一行或一列,并在这行或这列上选择两个端点网格,把以这两个网格为端点的区间内的所有网格染黑(包含这两个端点)。问经过n次操作之后,共有多少个格子被染黑,显然在众多操作中,很容易重复染色同一个格子,这个时候只计数一次。

输入
输入第一行包含一个正整数n(1<=n<=10000).

接下来n行,每行四个整数,x1,y1,x2,y2,分别表示一个操作的两端格子坐标。(-10^9<=x1,y1,x2,y2<=10^9),若x1=x2则是在一列上染色,若y1=y2,则是在一行上染色,保证每次操作是在一行或一列上染色。 

输出
输出仅包含一个正整数,表示被染色的格子的数量。

样例输入
3
1 2 3 2
2 5 2 3
1 4 3 4
样例输出
8

算法:

矩形面积并,扫描线+离散化+线段树

参考链接:https://www.jianshu.com/p/d70f6d346913

但是本题目与之不同,

1. 本题目中,数值为整数;poj1151中数值为浮点值;

2. 本题目中,每一个整数坐标表示一个格子,每个单位格子面积为1;poj1151中每个浮点坐标表示一个点,点的面积为0;

在算法实现上,有以下变动

1. 输入坐标的转换

在读入数据时,将格子坐标转成了点坐标,具体实现就是:

对于(x1, y1, x2, y2)的输入,其在坐标系中的端点坐标为:(x1 - 1, y1, x2, y2 - 1);并以此新坐标作为line的输入。

其中flag为lines[i]的标记符号,covered + flag记录x轴坐标的覆盖次数:

1)flag = 1,表示这条线为底部的线,扫描这条线则进入矩形区域,该线段上的所有横坐标覆盖次数 + 1,

2)flag = -1, 表示这条线为顶部的线,扫描这条线则退出矩形区域,该线段上的所有横坐标覆盖次数将 -1,

2. 线段树的构造

在poj1151中,线段树的每个节点表示一段区间:节点[1, 5) 表示真实区间 [0, 4)等;

下面代码中,将左闭右开的区间表示,转换为左闭右闭的表示:节点[1, 4] 表示真实区间 [0, 3]等;

因而,poj1151中的叶子节点为 [l, r), 其中 r == l + 1; 而下面代码中的叶子节点为 [l, r], 其中 r == l;

3. 线段树cnt的计算

每个节点的cnt的值,为其孩子节点cnt的值的加和,

在poj1151中,对于叶子节点,若被覆盖,则其cnt的值为segTree[t].real_r - segTree[t].real_l,表示这个左闭右开的区间长度;

在下面代码中,由于每个叶子节点只表示坐标系中的一个点,若其被cover,则该节点的cnt  = 0.5。为什么设置为0.5呢?以本题目样例说明:

3
1 2 3 2
2 5 2 3
1 4 3 4

则转换后的各条扫描线分别为:

lines[1]: y = 1, [x1, x2] = [0, 3], flag = 1;

lines[2]: y = 2, [x1, x2] = [0, 3], flag = -1;

lines[3]: y = 2, [x1, x2] = [1, 2], flag = 1;

lines[4]: y = 3, [x1, x2] = [0, 3], flag = 1;

lines[5]: y = 4, [x1, x2] = [0, 3], flag = -1;

lines[6]: y = 5, [x1, x2] = [1, 2], flag = -1;

            图1. 线段树

(图中矩形框中的[l ,r] 表示线段树节点的左右端点序号范围;每个矩形框右边的[real_l, real_r] 表示该节点对应的真实的左右端点x轴坐标范围。橙色标注的序号t表示线段树的第t个节点。)

对于lines[3], 该线段覆盖区域为[1, 2], 该区域长度为1;即上图中节点5, 6均被覆盖,则每个节点的cnt值应该设置为单位区间长度的一半,为0.5.

代码:

  1 # include <bits/stdc++.h>
  2 using namespace std;
  3
  4 const int maxn = 1e5;
  5 int n;
  6 set<int> x_set;
  7 set<int>::iterator it;
  8 int x[maxn * 2 + 10];
  9
 10 struct TreeNode
 11 {
 12     int l, r;
 13     int real_l, real_r;
 14     int covered; // covered times
 15     double cnt; // total covered length in this zone
 16 }segTree[maxn * 2 + 10];
 17
 18 struct Line
 19 {
 20     int y;
 21     int x1, x2;
 22     int flag;
 23 }lines[maxn * 2 + 10];
 24
 25 bool cmp(Line a, Line b)
 26 {
 27     return a.y < b.y;
 28 }
 29
 30 void build(int t, int l, int r)
 31 {
 32     segTree[t].l = l;
 33     segTree[t].r = r;
 34     segTree[t].real_l = x[l];
 35     segTree[t].real_r = x[r];
 36
 37     segTree[t].covered = 0;
 38     segTree[t].cnt = 0;
 39
 40     if(l == r) return;
 41     int mid = (l + r) >> 1;
 42     build(t << 1, l, mid);
 43     build(t << 1 | 1, mid + 1, r);
 44 }
 45
 46 void calen(int t)
 47 {
 48     if(segTree[t].covered > 0)
 49     {
 50         double tmp = 0;
 51         if(segTree[t].l == segTree[t].r) tmp = 0.5;
 52         segTree[t].cnt = segTree[t].real_r - segTree[t].real_l + tmp;
 53         return;
 54     }
 55
 56     if(segTree[t].r - segTree[t].l == 0) segTree[t].cnt = 0 ;
 57     else segTree[t].cnt = segTree[t << 1].cnt + segTree[t << 1 | 1].cnt;
 58 }
 59
 60 void update(int t, Line line)
 61 {
 62     // line.x1 ~ line.x2 covered segTree[t].real_l ~ real_r => add 1
 63     if(segTree[t].real_l >= line.x1 && segTree[t].real_r <= line.x2)
 64     {
 65         segTree[t].covered += line.flag;
 66         calen(t);
 67         return;
 68     }
 69
 70     // line not in the segTree[t] covered line
 71     if(segTree[t].real_l > line.x2 || segTree[t].real_r < line.x1) return;
 72
 73     // update the child node
 74     update(t << 1, line);
 75     update(t << 1 | 1, line);
 76     calen(t); // 每个节点的cnt的值为其孩子节点cnt的值的加和, 更新完孩子节点cnt后要更新t节点cnt
 77 }
 78
 79
 80 int main()
 81 {
 82     int x1, x2, y1, y2;
 83     while(cin >> n)
 84     {
 85         int t = 1;
 86         for(int i = 1; i <= n; ++i)
 87         {
 88             scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
 89             lines[t].y = y1;
 90             lines[t].x1 = x1 - 1;
 91             lines[t].x2 = x2;
 92             lines[t].flag = -1;
 93             x_set.insert(x1 - 1);
 94             ++t;
 95
 96             lines[t].y = y2 - 1;
 97             lines[t].x1 = x1 - 1;
 98             lines[t].x2 = x2;
 99             lines[t].flag = 1;
100             x_set.insert(x2);
101             ++t;
102         }
103         sort(lines + 1, lines + t, cmp);
104
105         int x_index = 1;
106         for(it = x_set.begin(); it != x_set.end(); ++it)
107         {
108             x[x_index++] = *it;
109         }
110         sort(x + 1, x + x_index);
111
112         build(1, 1, x_index - 1);
113
114         int res = 0;
115         update(1, lines[1]);
116         for(int i = 2; i < t; ++i)
117         {
118             int tmp = (lines[i].y - lines[i - 1].y) * segTree[1].cnt;
119             res += tmp;
120             update(1, lines[i]);
121         }
122
123         cout << res << endl;
124     }
125     return 0;
126 }

原文地址:https://www.cnblogs.com/shiyublog/p/10783164.html

时间: 2024-10-28 23:22:43

[2019 美团春招实习笔试] 2. 染色格子数量的相关文章

百度2016笔试(算法春招实习)

4.23 10:00更新.编程题1的Python实现.仅供參考.源代码见页尾 4.23 20:35更新,编程题2的Python实现.源代码见尾页 百度的题还是很偏重算法的.总体来讲难度比較高.尤其是编程题,以下附上原题: 选择题 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" > 问答题 主观题 wat

去哪儿网2017春招在线笔试

第一题,给二叉树的先序遍历和中序遍历,求层序遍历. 这个...大二做的... 根据先序和中序的性质,先序第一个节点一定是根,在中序中找到根的位置,左边的就是左子树,右边的就是右子树,递归就好. 递归建好树  层先遍历需要通过队列实现. #include <bits/stdc++.h> using namespace std; struct bi_tree { int val; bi_tree *left, *right; }; bi_tree *build_tree(int a[], int

2020 春招 华为笔试 2月26日

时间是两个小时,总共三道编程题目. 第一道题目大意: 输入一个int类型的数,判断它的比特流中有多少个“010”,及第一个“101”的下标(这个下标是从低位向高位数的). 如:输入:21 输出   2 0 原因:21  二进制表示为  0000 0000 0000 0000 0000 0000 0001 0101    总共两个“101”(两个“101”可以重叠), 且第一个下标为0,第二个下标为2,所以返回2 0 提交代码如下 1 int main(){ 2 int num; 3 while(

笔试-2020C++前端乐元素上海2020届春招技术笔试(什么时候投了个前端。。。。。)

感悟:总体分为选择题20,填空题20,分析题(不想做了,直接交卷了),总体考察数据库和C++语法函数偏多,感觉数据库收获挺多的. 选择题:第一二题是关系数据库,windows的I/O之类 下列不是描述类的成员函数的是 A.构造函数 B.析构函数 C.友元函数 D.拷贝构造函数 C 友元函数是独立于当前类的外部函数,但它可以访问该类的所有对象的成员:拷贝构造函数是使用已存在的对象初始化正在生成的对象时调用的成员函数. C内存分配:堆.栈.静态存储区(全局变量,static变量,常量) C程序内存:

2019春招、秋招总结【未加密】

这周接到了滴滴的薪资电话,只等签三方了,我秋招就算结束了,一个很满意的结果,薪资满意,而且10-7-5的工作时间很满意. 其实我九月底就没有继续面试笔试了,在准备毕业的事情,总结也是拖了很久一直想写,一直没写.列个流水账总结一下春招实习到秋招找工作的各种事情. 研究生一直在做机械臂的东西,后面为了找工作研究了一年视觉的东西.所以找工作的目标也是计算机视觉.春招实习被腾讯自动驾驶组招进去,实习了快三个月,主要内容是标定相关,自己太弱了,没法留在腾讯.秋招继续找工作,借着腾讯自动驾驶实习的"噱头&q

[找工作] 2019秋招|从春招到秋招,Java岗经验总结(收获AT)

转自(有更多) https://blog.csdn.net/zj15527620802/article/month/2018/10 前言 找工作是一件辛酸而又难忘的历程.经历过焦虑.等待.希望,我们最终都能去到属于自己理想的地方. 从春招到秋招,我总计面试了数十家公司.春招面试了腾讯.去哪儿.360.小米.携程.华为.今日头条.美团.菜鸟网络等9家公司.除了今日头条止步三面.菜鸟物流备胎至七月底才回绝,其他公司均通过了面试.最后在美团和腾讯中纠结了一段时间后,选择了腾讯去实习.秋招腾讯转正的还算

网易2017春招笔试真题编程题集合题解

01 双核处理 题目 一种双核CPU的两个核能够同时的处理任务,现在有n个已知数据量的任务需要交给CPU处理,假设已知CPU的每个核1秒可以处理1kb,每个核同时只能处理一项任务.n个任务可以按照任意顺序放入CPU进行处理,现在需要设计一个方案让CPU处理完这批任务所需的时间最少,求这个最小的时间. 输入描述 输入包括两行:第一行为整数n(1 ≤ n ≤ 50)第二行为n个整数lengthi,表示每个任务的长度为length[i]kb,每个数均为1024的倍数. 输出描述输出一个整数,表示最少需

错过了春招的你,难道还不为秋招做准备吗

序 如今的校招,像一场突围赛,很多人"牺牲"在前进的路上,那些突围成功的人,不是因为他们刀枪不入,而是他们有扎实的准备,周密的计划以及可圈可点的技巧. 获得校招的成功,大致需要经历计划.准备.应聘这三个过程,文中全面覆盖这些内容. 无论你是刚刚进入大学的萌新,还是身处在大二.大三的任何阶段,这篇文章都尽可能给出一些建议和指导. 目录 简单的自我介绍 [计划]时间安排:对找工作的同学十分重要的时间点,以及如何安排自己的行程 [准备]技能清单:需要具备的能力 [准备]如何提高:如何有针对性

错过了春招,难道你还不为秋招做准备吗

序 如今的校招,像一场突围赛,很多人"牺牲"在前进的路上,那些突围成功的人,不是因为他们刀枪不入,而是他们有扎实的准备,周密的计划以及可圈可点的技巧. 获得校招的成功,大致需要经历计划.准备.应聘这三个过程,文中全面覆盖这些内容. 无论你是刚刚进入大学的萌新,还是身处在大二.大三的任何阶段,这篇文章都尽可能给出一些建议和指导. 目录 简单的自我介绍 [计划]时间安排:对找工作的同学十分重要的时间点,以及如何安排自己的行程 [准备]技能清单:需要具备的能力 [准备]如何提高:如何有针对性