Medium
题目描述
给你一个非负整数数组 nums 和一个整数 k。在一次操作中,你可以选择 nums 中的任意一个元素并将它增加 1。
请你返回在 至多 k 次操作之后,能够得到的 nums 的最大乘积。由于答案可能很大,请你将答案对 10^9 + 7 取余后返回。
注意,你需要将乘积最大化之后再取余。
示例 1:
输入:nums = [0,4], k = 5
输出:20
解释:将第一个数字增加 5 次。
现在 nums = [5, 4],乘积为 5 * 4 = 20。
可以证明 20 是能够得到的最大乘积,所以我们返回 20。
注意,可能有其他方法将 nums 增加到最大乘积。
示例 2:
输入:nums = [6,3,3,2], k = 2
输出:216
解释:将第二个数字增加 1 次,将第四个数字增加 1 次。
现在 nums = [6, 4, 3, 3],乘积为 6 * 4 * 3 * 3 = 216。
可以证明 216 是能够得到的最大乘积,所以我们返回 216。
注意,可能有其他方法将 nums 增加到最大乘积。
约束条件:
1 <= nums.length, k <= 10^50 <= nums[i] <= 10^6
解题思路
解题思路
要使数组的乘积最大,我们需要采用贪心策略。关键观察是:每次都应该优先增加最小的数字。
为什么要优先增加最小值?
考虑两个数字 a 和 b (其中 a ≤ b),如果我们只能增加一个数字:
- 如果增加 a,乘积变化为
(a+1) * b - a * b = b - 如果增加 b,乘积变化为
a * (b+1) - a * b = a
由于 a ≤ b,所以增加较小的数字能带来更大的乘积增长。
算法步骤
- 使用最小堆存储所有数字
- 进行 k 次操作,每次:
- 取出堆顶(最小值)
- 将其加 1 后重新放入堆中
- 计算最终所有数字的乘积,注意取模
这个贪心策略保证了每一步都能获得最大的乘积增长,从而得到全局最优解。
时间复杂度为 O(k log n),其中建堆需要 O(n),k 次堆操作各需要 O(log n)。
代码实现
class Solution {
public:
int maximumProduct(vector<int>& nums, int k) {
const int MOD = 1000000007;
// 使用最小堆
priority_queue<int, vector<int>, greater<int>> minHeap;
for (int num : nums) {
minHeap.push(num);
}
// 进行k次操作
for (int i = 0; i < k; i++) {
int smallest = minHeap.top();
minHeap.pop();
minHeap.push(smallest + 1);
}
// 计算乘积
long long product = 1;
while (!minHeap.empty()) {
product = (product * minHeap.top()) % MOD;
minHeap.pop();
}
return product;
}
};
class Solution:
def maximumProduct(self, nums: List[int], k: int) -> int:
import heapq
MOD = 10**9 + 7
# 建立最小堆
heapq.heapify(nums)
# 进行k次操作
for _ in range(k):
smallest = heapq.heappop(nums)
heapq.heappush(nums, smallest + 1)
# 计算乘积
product = 1
for num in nums:
product = (product * num) % MOD
return product
public class Solution {
public int MaximumProduct(int[] nums, int k) {
const int MOD = 1000000007;
// 使用最小堆
var minHeap = new PriorityQueue<int, int>();
foreach (int num in nums) {
minHeap.Enqueue(num, num);
}
// 进行k次操作
for (int i = 0; i < k; i++) {
int smallest = minHeap.Dequeue();
minHeap.Enqueue(smallest + 1, smallest + 1);
}
// 计算乘积
long product = 1;
while (minHeap.Count > 0) {
product = (product * minHeap.Dequeue()) % MOD;
}
return (int)product;
}
}
var maximumProduct = function(nums, k) {
const MOD = 1000000007;
const minHeap = new MinPriorityQueue();
for (let num of nums) {
minHeap.enqueue(num);
}
for (let i = 0; i < k; i++) {
const min = minHeap.dequeue().element;
minHeap.enqueue(min + 1);
}
let product = 1;
while (!minHeap.isEmpty()) {
product = (product * minHeap.dequeue().element) % MOD;
}
return product;
};
复杂度分析
| 操作 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 建堆 | O(n) | O(n) |
| k次堆操作 | O(k log n) | O(1) |
| 总体 | O(n + k log n) | O(n) |
其中 n 是数组长度,k 是操作次数。空间复杂度主要用于存储堆结构。