题意简述
一个大小为$n$的方阵,每行都选一个数,得到一个和。一共能得到$n^n$个和。求出最小的前$n$个。重复的算多次。$n<=700$。
思路框架
每两行都来一个多路归并即珂。
具体思路
今天去看了刘汝佳的小蓝书。感谢刘汝佳,让我知道优先队列原来还能这么用。
首先,每行里面顺序不重要,先排序再说。
前置知识:n路归并问题
我们应该都知道“二路归并”问题:两个有序的序列,要合并成一个有序的序列。解法很简单,维护两个指针,哪个小就把哪个加入答案,然后让指针++。利用它,我们珂以写出大名鼎鼎的「归并排序」算法。
$n$路归并问题就是:$n$个序列,每个序列都是有序的。要合并成一个长度为$n^2$的有序的序列。
但是我们发现,我们要维护$n$个指针,一次维护指针是$O(n)$的,我们一共有$n^2$的元素,就变成$O(n^3)$了。怎么样更快呢?我们珂以用优先队列来找到值最小的那个指针。这样维护一次指针是$O(logn)$的,总共就是$O(n^2logn)$的了。而且原问题中我们只需要求前面的$n$个,所以这会更快,变成每次$O(nlogn)$的时间。
如何转化
我们的原问题是有$n$行$n^n$个和的,如何转化?
首先我们珂以每两行之间逐个合并。然后对于两行$A$和$B$的问题,我们只需要生成这样$n$个序列即珂:
第一个:$A[1]+B[1],A[1]+B[2],…A[1]+B[n]$。
第二个:$A[2]+B[1],A[2]+B[2],…A[2]+B[n]$。
…
第$n$个:$A[n]+B[1],A[n]+B[2]…A[n]+B[n]$。
然后跑一个$n$路归并即珂。这样做$n-1$次,每次都只求前面$n$个小的,我们就珂以求出$n^n$个和中最小的$n$个了。
实现细节:关于如何维护指针
如果您足够强,您完全不用看这个。
我们维护两个值,$s,b$。$s$表示目前的$A[a]+B[b]$,$b$表示在$B$中的下标。我们完全不用知道$A$中的下标,因为$A[a]$是不会改变的。我们有$(s,b)$,下一个指针$(s’,b’)$其实就是$(s-B[b]+B[b+1],b+1)$。
在优先队列中,我们显然是要以$s$为关键字判断优先级的。
—— 刘汝佳的思路
代码
1 |
|