支线任务-5

这次的题目是

The Skyline Problem

就是给几出几个矩形,让你描述出它们的轮廓线。

 

大概就是这样

听说这个题目很麻烦,标注也是hard……然而认真的读完题后发现……这不就是随便一个区间处理裸题么……而且还是一维的……三分钟想出两个方法并不是很困难的事情,可能我实力没那么强没想出第三个方法。

个人感觉(或者通过看其他同学的博客,和他们聊观察到的他们的感觉),这个题目并没有难度(甚至不如前面某次难),如果说这个题目已经是leetcode最麻烦最难的题目希望能够换一个题库进行任务布置。

好了废话不多说,拿到题目首先有两个想法。

一:既然是区间处理,那么第一秒想到了线段树,然而觉得用这种区间处理的神级数据结构来完成这种任务简直太大材小用了。

二:改变思路,感觉这种问题很像那种给你几个区间和其权值让你求所有点的权值和的前缀和的问题。所以改一改前缀和的方法应该就能完成这个题目,然后在纸上算了算发现果然可行。

下面我对这两个方法做一个解释。


线段树:

假设区间长度为N,线段数会把N分成2,4,8……next2pow(N)这么几层来进行管理。使得所有对区间的修改全部降为O( Log( N ) )级别同时会发现空间只是扩充了一倍,依然是O( N )。基本用来处理所有的区间问题(包括二维)都非常给力,包括区间加权区间删除区间查找,甚至是区间合并等操作……可以说是秒杀很多区间处理问题的神级数据结构,当然本题无非也就用到了其中的添加和查找两个操作,自然处理起来毫无压力,不可多得的数据结构。

在此给出一个讲得不错的线段树的博客,个人感觉代码和样例都十分生动形象

[转]@Microgoogle

线段树segment tree

 

前缀和:

所谓前缀和就是

对于每个区间{ [ ai, bi ], wi },

w[ ai ] += wi,

w[ bi ] -= wi,

而每个点最终点权值就是w[ i ] = sum( [ w[ x ] for x in range( 0, i ) ] )

我们修改一下这个算法,显然每个点只需保留当前最高的一栋楼的高度,

所以变为:

对于每个区间{ [ ai, bi ], wi }

w[ ai ].append( wi ),

w[ bi ].append( wi ),

维护一个支持删除操作的优先队列 priorityQueue

对于每个点 i 有三种情况,分别采用如下算法

  1. w[i].empty(), continue
  2. for j in length( w[ i ] ), if w[ i ][ j ] > 0, priorityQueue.insert( w[ i ][ j ] )
  3. for j in length( w[ i ] ), if w[ i ][ j ] < 0, priorityQueue.delete( -w[ i ][ j ] )

而每个点 i 的权值就是

  1. priorityQueue.empty()      -->  0
  2. not priorityQueue.empty() -->   priorityQueue.top()

如果优先队列能在O( Log( N ) )内完成插入和删除的话,最终结果就会是O( N Log( N ) ),和线段树是一样的。


总结

希望看到这个博客的人能学到点东西吧,比如至少要能会写线段树。

代码就不贴了,线段树就是裸的,前缀和的话都不到40行

我认为像这次这样的题目也就是让同学们认识到了线段树的存在(虽然估计很多用了前缀和方法的还是没能了解到线段树),并不能让同学们有什么提高,而且说真的,我自己做完了也没有得到什么提高。既然说到了这项任务不是竞赛选手的竞技场,我认为任务布置应该更加贴切课内知识,比如上次那道用栈的题目就非常好,我也从那个题目里学到了不少知识。如果说用高级数据结构和类似LCA算法的题目是竞赛知识的话,那不如把支线任务的重点放在开发方面(而不是leetcode这种奇怪的题库),实现一些能够把课上知识应用起来的小工程(而不是做一些质量并不高的半竞赛不竞赛的题)。毕竟,让同学们能在支线任务中学到东西不才是最重要的么?

原文地址:https://www.cnblogs.com/lustralisk/p/branch4.html