Problem Address:http://poj.org/problem?id=2442
这道题以前看过,但是WA了,自己也知道为什么WA,但就是想不出好的办法。
最近也是在徘徊阶段,做ACM的情绪不稳定。最后这道题被我做出来了,还是给了我不少信心和勇气。
今天这道题总共交了三次。前两次都是TLE。
前两次都是想通过维护一个最小堆来取得结果的n个数,但是后来想想,维护最小堆的时候,如果插入了一个数,并不能保证这前n个数就是最小的n个数。所以通过一些比较麻烦的做法去实现。第二次用了set,还是一样的TLE。
然后我决定放弃了。把VC++关掉,去上厕所。但是就在上厕所的一瞬间= =(我这个人不得了……)想换种思路,想用最大堆如何呢?然后居然就一下子想通了。打着代码的时候发现原来这就是Discuss里说的那种思路。
看别人写着一些字母阿拉伯数字什么的,看起来都挺晦涩的。所以还是尝试用文字叙述一下为好。
以下说思路:
m行的数。
拿第一行作最大堆。然后开始拿第二行。
我们的目的是要把这两行交叉相加得到n*n个数并取前n个数。
所以先拿第二行的第一个数跟第一个最大堆里的n个数相加得到n个新的数放在第二个最大堆里。然后从第二行的第二个数开始分别与第一个堆相加并插入到第二个堆中。
【重点】由于第二个堆是最大堆,每插入一个数,如果这个数比最大堆的顶(这个数在这个堆中最大)大,说明这个数不能插入进来。如果比堆顶小,说明应该把堆顶换出去,而把这个数插入进来。(这一步可以通过用数置换堆顶并向下维护堆来实现)
这样两行过后就可以得到一个最大堆,这个堆是这两行相加的所有数中最小的n个数,其堆顶就是最小的n个数中的最大值。
然后重复上面的步骤直到结束。
出结果。
【技巧】可以用指针指向堆,并在每两行相加后交换指针。(这个看个人喜好,也可以定义一个二维数组然后用flag不断变换)
【总结】发现可以用堆维护一个数列里最大或最小的k个数。用最大堆可以维护最小的k个数,用最小堆可以维护最大的k个数。但是堆中的数,除了堆顶的数是最值之外,其他的数都没有特别的大小关系,也就是说里面的数是无序的。
以下贴代码:
最近学院要办新手赛了。这周热身下周比赛。
我这位刚入门不久的大菜鸟也参与了此次比赛的出题。
话说有奖品T_T我也想参加的说……我问XJL大二能不能参加,他说“能,你不能”……
而且最近热衷于宣传ACM,在直系里拉了一个队,连我同班的同学也不放过……
好吧,就这样吧。接下来要好好努力。向未来勇敢前进!