leetcode
leetcode 2301 ~ 2350
k-avoiding 数组的最小总和

k-avoiding 数组的最小总和

难度:

标签:

题目描述

You are given two integers, n and k.

An array of distinct positive integers is called a k-avoiding array if there does not exist any pair of distinct elements that sum to k.

Return the minimum possible sum of a k-avoiding array of length n.

 

Example 1:

Input: n = 5, k = 4
Output: 18
Explanation: Consider the k-avoiding array [1,2,4,5,6], which has a sum of 18.
It can be proven that there is no k-avoiding array with a sum less than 18.

Example 2:

Input: n = 2, k = 6
Output: 3
Explanation: We can construct the array [1,2], which has a sum of 3.
It can be proven that there is no k-avoiding array with a sum less than 3.

 

Constraints:

  • 1 <= n, k <= 50

代码结果

运行时间: 23 ms, 内存: 16.1 MB


// 思路:与普通Java版本类似,我们使用流来生成满足条件的数组。我们从1开始遍历,每次检查当前数字是否可以加入数组,如果满足条件则将其加入,最终求和返回。我们将遍历过程用Stream API来实现。

import java.util.stream.IntStream;

public class Solution {
    public int minimalSum(int n, int k) {
        return IntStream.iterate(1, i -> i + 1) // 从1开始的无限流
                .filter(current -> IntStream.range(1, current / 2 + 1) // 过滤掉那些不能加入的数字
                        .allMatch(i -> current - i != k - i))
                .limit(n) // 限制流的大小为n
                .sum(); // 求和
    }
}

解释

方法:

要构造一个长度为 n 的 k-avoiding 数组,我们需要避免任何两个元素之和恰好为 k。解决方案的关键在于理解两个数相加等于 k 的可能性。\n\n首先,计算 k/2 的整数部分,记为 mid。这个值是能够与另一个数相加可能等于 k 的最大值。\n\n1. 如果 n 小于等于 mid,那么我们可以直接取最小的 n 个正整数,即 [1, 2, ..., n],因为在这个范围内任何两数之和都不会等于 k。\n\n2. 如果 n 大于 mid,我们需要考虑如何安排超出 mid 的那些数。首先我们取 [1, 2, ..., mid],然后从 k 开始取数,避免与前面的数相加等于 k。具体来说,从 k 取到 k + (n - mid - 1),确保不会有和为 k 的组合。\n\n因此,数组的构造分为两部分:前半部分直接取最小的 mid 个数,后半部分从 k 开始取,避免和前半部分的数相加得到 k。

时间复杂度:

O(1)

空间复杂度:

O(1)

代码细节讲解

🦆
在考虑k/2的整数部分时,即变量`mid`,为什么这个值是决定数组构造策略的关键?
考虑 `mid` 即 `k/2` 的整数部分是关键,因为任何两个小于或等于 `mid` 的正整数相加都不会等于 `k`。这是因为最大可能的和是 `mid + mid`,这等于 `k`(当 `k` 为偶数时)或小于 `k`(当 `k` 为奇数时)。因此,只要我们使用的数字都小于或等于 `mid`,就可以保证没有任何两个数字的和等于 `k`。这个逻辑帮助我们构造出前半部分的数组,确保不会违反 k-avoiding 的条件。
🦆
对于`n`大于`mid`的情况,你是如何确定从`k`开始取数,而不是从其他较大的数开始取数?
当 `n` 超过 `mid` 时,从 `k` 开始取数而不是更大的数,是为了确保数组的剩余部分与前半部分中的任何数相加都不会产生和为 `k` 的情况。从 `k` 开始取数可以最大化地利用数字范围,同时避免与前半部分 `[1, 2, ..., mid]` 中的数相加得到 `k`。如果从比 `k` 更大的数开始,可能会导致数字范围不够用,或者不必要地增加数组元素的总和。
🦆
为什么在计算从`k`到`k + (n - mid - 1)`的数的和时采用了特定的公式`(k + (k + n - mid - 1)) * (n - mid) // 2`?请解释这个公式的来源和合理性。
这个公式是等差数列求和的标准公式。在这里,我们从 `k` 开始,到 `k + (n - mid - 1)` 结束,共有 `n - mid` 个数。这个序列的首项是 `k`,末项是 `k + (n - mid - 1)`,因此其和可以通过求等差数列和的公式计算:`(首项 + 末项) * 项数 / 2`。这种方法确保了计算的准确性和效率,同时满足了题目要求避免和为 `k` 的情况。
🦆
如果存在某些特定的`k`和`n`组合,例如`k`非常接近`n`,这种构造方法是否仍然有效?是否会出现无法避免求和等于`k`的情况?
此方法在大多数情况下有效,但当 `k` 非常接近 `n` 时,特别是 `k` 小于或等于 `n` 时,可能会遇到问题。例如,如果 `k` 等于 `n`,那么从 `1` 到 `n` 的数每两个数相加都可能得到 `k`。在这种极端情况下,我们可能需要考虑不同的策略或确认给定的 `k` 和 `n` 是否适用于此方法。如果 `k` 非常小,可能不存在完全避免和为 `k` 的构造方法,因为可用的数字范围有限。

相关问题