MIT6.006 有趣习题整理
1. Problem Season3 P3-3 (d)
简而言之, 让我们找到最便捷的排序方法. 数据是 \(n\) 个比值: \(\frac{w_i}{f_i}\) , 其中: \(w_i,f_i\in(0,n^2]\) 且为整数(没有重复元素)
很自然的, 我们第一时间想到, 使用merge sort等排序方法, 很便捷的就可以在 \(O(n\log n)\) 时间内排序完成(简化的, 在比较两比值时有 \(\frac{w_i}{f_i}>\frac{w_i}{f_i}\Leftrightarrow w_if_j>w_jf_i\), 更精确的比较并且省内存)
(这种方法值这道题的4/5分)
但是还有另一种方法, 以线性时间排序:
我们考虑能不能通过将这个比值乘以一个大数 \(k\) (最好是 \(n\) 的幂, 这样在radix sort时, 可以做到线性时间), 然后用radix sort排序其上取整后的整数呢
也即我们排序 \(\{\lceil k\cdot\frac{w_i}{f_i} \rceil\}\) 这个新整数数列, 很显然只需要用radix sort, 花费 \(O(n + n\log_n k)\) 的时间复杂度.
现在我们只需要找到满足“令新数列的顺序=原数列的顺序”的k值即可.
也即 \(\lceil k\cdot\frac{w_i}{f_i} \rceil\) 的顺序等价于 \(\frac{w_i}{f_i}\)
若 \(\lceil k\cdot\frac{w_i}{f_i} \rceil > \lceil k\cdot\frac{w_j}{f_j} \rceil\), 那么很显然, \(k\cdot\frac{w_i}{f_i}>k\cdot\frac{w_j}{f_j}\)
由此, 我们可知, 新数列中的大小关系一定代表了原数列的大小关系. 但是还有一种可能, 原数列的两个数对应的新数列中的值相等(两个数在同一对相邻整数之间)
也即
我们可以创造一个令上式不存在的充分条件:也即令:
故我们只需令:
也即我们令 \(k=n^4\) 时, 满足原条件.
而新的时间复杂度为 \(O(n+n\log_n n^4)=O(n)\) , 为线性时间复杂度.
2. Problem Season3 P3-4
有一个整数集 \(S:\{s_1, s_2, ...s_n\}\), 其中每个元素均为区间 \([0,h-1]\) 内的整数.
(a) 请在 \(O(n)\) 时间内找出区间内和为 \(h\) 的两个数, 或者不存在.
(b) 我们假设不存在满足(a)的数, 请在最坏 \(O(n)\) 时间内找出和离 \(h\) 最近且不大于 \(h\) 的两个数.
(a)
很常规的, 我们会想到俩俩加和看是否等于 \(h\), 这样的时间复杂度在 \(O(n^2)\)级别 (因为最坏需要比较所有元素, 总次数为 \(\binom{n}{2}=\frac{n(n+1)}{2}\))
但对于每个 \(s_i\), 其如果想凑出 \(h\), 只需要找到数列中有没有 \(h-s_i\) , 也即只要对每个 \(s_i\)实现能常数时间查找有无 \(h-s_i\), 即可判断出数列中有无符合条件的数.
最简单的, 我们先将 S 存储进一个哈希表, 需要 \(O(n)\) 时间, 然后再对每个元素进行查找, 就可以以 \(O(n)\) 时间解决该问题. 但是基于总复杂度 \(O(n)\) 不变的条件下, 我们可以继续优化代码:
我们直接开始遍历 S, 对于每个 \(s_i\) , 我们查找它是否在哈希表中, 如果存在则说明 \(s_i\) 与 \(h-s_i\) 存在于 S 中, 是符合条件的两个数, 如果不存在则将 \(s_i\) 存进哈希表, 并继续.
--2025.7.26
(b)
本人的第一想法是, 能不能用类似(a)的做法, 比如使用一个长度为 \(h\) 的数组, 以类似dp的形式储存目前可以达到的小于 \(h\) 的最大距离. 不过考虑到 \(h=600n^2\) , 我们无法做到 \(O(n)\) 的效率.
注意到数据范围 \(h=O(n^2)\), 故我们先对这 \(n\) 个数据进行基数排序, 消耗时间
这样我们得到了一个有序的数组, 并且由于渐进的加法性质, 我们接下来寻找的时间只要也是 \(O(n)\), 从时间就是 \(O(n)\) .
我们考虑如下寻找方式:
两pointer a=A[0] b=A[n-1], 记录当前 \((h-a-b)\)
若为正, 则记录最小值, 并使 a 向右遍历;
若为负, 则使 b 向左遍历, 直至为正
等到 a==b 时, 我们先前的到的最小正数 \((h-a-b)\) 就是我们需要的组合.
我们接下来证明不会有最佳组合无法遍历到
对于使得 \((h-a-b)\) 最小的 (a, b) , 我们有:
- 对于所有在
a前面的元素, 由于数组有序(递增), 其与b的和只会更小, 故在排序到组合(a',b),a'<a时, 会使a指针不断向右遍历直至最佳组合 ; - 对于所有在
b后面的元素, 其大于b且 (a,b) 是最佳组合, 故其与a的和均大于 \(h\) , 会被跳过直至遍历到最佳组合
--2025.7.31
浙公网安备 33010602011771号