问题描述
小C和小U有一个从0开始的数组nums,以及一个非负整数k。每次操作中,小C可以选择一个尚未选择的下标i(范围在 [0, nums.length - 1]),然后将nums[i]替换为[nums[i] - k, nums[i] + k]之间的任意整数(包含边界)。在应用任意次数的操作后,返回数组nums可能达到的最大分数。数组的分数被定义为数组中最多重复的元素个数。注意,每个下标只能被操作一次。
难点分析
- 操作选择范围:每次操作可以将
nums[i]改为[nums[i] - k, nums[i] + k]之间的任意值,这个调整范围较大,增加了策略选择的复杂性。 - 最大分数定义:分数是数组中最多重复的元素个数,因此优化目标是让尽可能多的数值变成相同的数。
- 区间合并问题:不同数值可以通过操作聚集成同一值,但需要判断哪些数值能被拉近,哪些无法合并。
- 操作顺序的影响:操作顺序会影响最终结果,需要找到最优的操作顺序,先合并接近的数值,避免错失最佳机会。
- 数据结构优化:频率统计和查询需要高效的数据结构,如哈希表或优先队列,才能快速获得最大重复值。
解题思路
根据以上分析,解决问题可以分为以下步骤:
1. 理解操作的灵活性
每次选择一个尚未操作的下标 i,我们能把 nums[i] 修改为一个区间中的值。**关键点在于:**对于每个数值 nums[i],它的修改结果会影响最终的最大重复次数。理论上,越多数字聚集在一个值上,分数就越高。那么,如何在众多选择中做出最合适的操作呢?
2. 区间合并策略
由于每个数字可以变动的范围是一个闭区间 [nums[i] - k, nums[i] + k],我们可以将所有数字的这些区间看作是一个区间合并问题。要点是:我们希望在所有可能的数值区间中找到重合部分,这些重合部分可以代表最终我们能够调整到的数字集合。如果两个数字的区间重叠,说明它们能通过适当的操作变得一致;反之,则无法直接合并。
3. 区间重叠判断
如何判断两个区间是否能重叠?简单来说,如果两个区间 [a1, b1] 和 [a2, b2] 满足 max(a1, a2) <= min(b1, b2),那么它们就有重叠部分,说明它们能聚合成一个公共数值。这一判断的目的,是帮助我们筛选出能聚合的数值范围,从而决定哪些数值能够共享相同的目标值。
4. 贪心选择操作顺序
在选择数字进行修改时,我们的目标是尽量将更多的数字调整到同一个值。因此,贪心策略可以作为解决方案的一部分。每次操作时,我们选择当前数值与其他数值重合的最大区间,让这些数值更有可能最终聚合。通过这样的策略,我们能尽可能让更多的数字相互“靠拢”,从而达到最大重复次数。
5. 频率统计
我们对每个操作的结果进行频率统计,最终根据每个可能的最终值的频次来计算分数。通过这种方式,我们可以从多个角度评估哪些值能达到最高的频次,进一步确定操作顺序和策略。
6. 动态规划与备选方案
如果贪心策略无法保证最优解,我们可以结合动态规划,评估在不同选择下的最佳分数。每一个选择都可能引发不同的后果,因此可以通过状态转移方程来更精确地计算最优解的最大值。这一步可以有效处理复杂的操作顺序和数字调整问题。
具体代码实现
以下是 JavaScript 的代码实现:
function solution(nums, k) {
// 创建一个字典来记录每个可能的元素值及其出现的次数
const count = {};
// 遍历数组中的每个元素
for (let num of nums) {
// 计算当前元素的可能替换值范围
for (let i = num - k; i <= num + k; i++) {
// 更新字典中每个可能替换值的出现次数
if (count[i] === undefined) {
count[i] = 1;
} else {
count[i]++;
}
}
}
// 找到字典中出现次数最多的值
let maxCount = 0;
for (let key in count) {
if (count[key] > maxCount) {
maxCount = count[key];
}
}
return maxCount;
}
function main() {
console.log(solution([4, 6, 1, 2], 2) === 3);
console.log(solution([1, 3, 5, 7], 1) === 2);
console.log(solution([1, 3, 5, 7], 3) === 4);
}
main();
python代码如下:
def solution(nums, k):
from collections import defaultdict
# 创建一个字典来记录每个可能的元素值及其出现的次数
count = defaultdict(int)
# 遍历数组中的每个元素
for num in nums:
# 计算当前元素的可能替换值范围
for i in range(num - k, num + k + 1):
# 更新字典中每个可能替换值的出现次数
count[i] += 1
# 找到字典中出现次数最多的值
max_count = max(count.values())
return max_count
if __name__ == "__main__":
print(solution([4, 6, 1, 2], 2) == 3)
print(solution([1, 3, 5, 7], 1) == 2)
print(solution([1, 3, 5, 7], 3) == 4)
复杂度分析
1.时间复杂度:(O(n))
2.空间复杂度:(O(n))
总结:
这道题的核心是如何通过调整数字,使得更多的数字聚集在同一个值上。在最初的实现过程中,我对如何高效地统计频率进行了很多尝试。最初我考虑过使用排序和二分查找,但最终发现直接用字典来存储可能值的频次更为简洁和直观。所以在实现过程中,我选择了一个字典来统计每个可能数值的出现次数。对于每个nums[i],我计算出了它可以调整到的所有值,并在字典中进行频次更新。最后,取字典中频次最大的值即为答案。