B002排序、双指针、哈希表、两数之和到K数之和(1640~1642)CSES?

摘要:1640 Sum of Two Values - CSES 1641 Sum of Three Values - CSES 1642 Sum of Four Values - CSES 这类问题可以抽象成 (K) 数之和,本质是递归的转
1640 Sum of Two Values - CSES 1641 Sum of Three Values - CSES 1642 Sum of Four Values - CSES 这类问题可以抽象成 \(K\) 数之和,本质是递归的转化为两数之和,时间复杂度为 \(N^{K-1}\) 。一般可以排序双指针或哈希表解决。 题意:给定一个 \(n\) 个整数的数组,找出 \(k\) 个不同位置(1-based)的元素,使其的和等于 \(x\) 。若无解输出 IMPOSSIBLE 。与力扣上的题目的有差异。 两数之和 因为需要输出位置,所以将原数组变成一个元组数组再排序。 排完序后数组具有单调性,前后两个指针的和与 \(x\) 比大小,小就 l ++ 大就 r -- 。 具体代码为 import sys from math import inf if 1: inp = lambda: sys.stdin.readline().strip() II = lambda: int(inp()) MII = lambda: map(int, inp().split()) LII = lambda: list(MII()) Max = lambda x, y: x if x > y else y Min = lambda x, y: x if x < y else y def main(): n, k = MII() a = LII() a = [(x, i+1) for i, x in enumerate(a)] a.sort() l, r = 0, n - 1 while l < r: s = a[l][0] + a[r][0] if s > k: r -= 1 elif s < k: l += 1 else: print(a[l][1], a[r][1]) return print("IMPOSSIBLE") if __name__ == "__main__": main() 还有更有学习意义的算法,即使用哈希表边枚举边查询。这个技巧很有用,很多场景用到的有很大的启发意义。 # 模版代码直接复制即可 def main(): n, k = MII() a = LII() mp = {} for i, x in enumerate(a): if k - x in mp: # 查是否遍历过 print(mp[k - x], i + 1) return mp[x] = i + 1 # 记录出遍历的部分 print("IMPOSSIBLE") 三数之和 在两数之和的外面套一层 for 循环转化求剩余区间的两数之和。 具体代码为 # 模版代码直接复制即可 def main(): n, k = MII() a = LII() a = [(x, i+1) for i, x in enumerate(a)] a.sort() for i in range(n): l, r = i + 1, n - 1 while l < r: s = a[l][0] + a[r][0] + a[i][0] if s > k: r -= 1 elif s < k: l += 1 else: print(a[i][1], a[l][1], a[r][1]) return print("IMPOSSIBLE") 四数之和 按照三数之和的思路套两层 for 循环时会超时,这时考虑剪枝和哈希表。 直觉告诉我们可以预先估计数的大小然后及时止损。 比如 2, 3, 5, 8, 11 和 x = 8 ,当 \(a_i=3\) 的时候已经没有往后遍历的必要了,因为有序数组在 \(i\) 以后的元素都大于等于 \(a_i\) ,所以这里最小也是 \(9\) ,不可能由等于 \(8\) 的机会了。
阅读全文