解决"k-avoiding 数组的最小总和"问题
这道题有两种主要解法。
解法一:直接数学计算(最优解)
通过数学推导直接计算出结果,不需要构建实际的数组。
class Solution:
def minimumSum(self, n: int, k: int) -> int:
# 特殊情况:当k很大或k=1时,可以直接选择前n个正整数
if k > 2*n or k <= 1:
return n * (n + 1) // 2
half_k = k // 2
chosen_count = min(n, half_k)
# 计算前half_k个数的和
first_sum = chosen_count * (chosen_count + 1) // 2
# 如果已经选择了n个数,直接返回结果
if chosen_count == n:
return first_sum
# 否则,计算从k开始的剩余数字的和
remaining_count = n - chosen_count
start = k
end = k + remaining_count - 1
remaining_sum = (start + end) * remaining_count // 2
return first_sum + remaining_sum
思路解析:
- 需要找到n个不同的正整数,使得没有两个数的和等于k,且总和最小。
- 为了最小化总和,总是尝试包含尽可能小的正整数。
- 对于任何一个数x,不能同时包含x和k-x(否则它们的和就是k)。
- 当x < k/2时,x < k-x,所以为了最小化总和,应该选择x而不是k-x。
- 因此,可以安全地包含1到k/2的所有数。
- 对于k/2 < x < k的数,它们的互补数k-x已经在选择的集合中(因为k-x < k/2),所以必须跳过这些数。
- 对于x ≥ k的数,它们的互补数k-x ≤ 0,不在正整数集合中,所以可以安全包含。
时间复杂度:O(1),只需要进行简单的数学计算。
空间复杂度:O(1),只使用常数空间。
解法二:贪心方法 + 集合
通过维护一个集合,贪心地选择最小的可行数字:
class Solution:
def minimumSum(self, n: int, k: int) -> int:
s = set()
num = 1
while len(s) < n:
if k - num not in s:
s.add(num)
num += 1
return sum(s)
思路解析:
- 维护一个已选择数字的集合s。
- 从1开始,尝试将每个数字加入序列。
- 对于每个数字num,检查其互补数(k-num)是否已在集合中。如果不在,则可以加入num。
- 继续此过程直到集合中有n个数字。
时间复杂度:O(n),最多需要检查2n个数字。
空间复杂度:O(n),需要存储n个数字。
示例分析
以示例1为例,n=5, k=4:
- 解法一中,half_k = 2,首先选择1和2。
- 然后跳过3(因为4-3=1,而1已经在集合中)。
- 然后从4开始选择剩余的数字:4,5,6。
- 最终序列是[1,2,4,5,6],总和为18。
两种解法都能得到正确结果,但第一种解法更高效,时间复杂度为常数级。