使子序列的和等于目标的最少操作次数
难度:
标签:
题目描述
You are given a 0-indexed array nums
consisting of non-negative powers of 2
, and an integer target
.
In one operation, you must apply the following changes to the array:
- Choose any element of the array
nums[i]
such thatnums[i] > 1
. - Remove
nums[i]
from the array. - Add two occurrences of
nums[i] / 2
to the end ofnums
.
Return the minimum number of operations you need to perform so that nums
contains a subsequence whose elements sum to target
. If it is impossible to obtain such a subsequence, return -1
.
A subsequence is an array that can be derived from another array by deleting some or no elements without changing the order of the remaining elements.
Example 1:
Input: nums = [1,2,8], target = 7 Output: 1 Explanation: In the first operation, we choose element nums[2]. The array becomes equal to nums = [1,2,4,4]. At this stage, nums contains the subsequence [1,2,4] which sums up to 7. It can be shown that there is no shorter sequence of operations that results in a subsequnce that sums up to 7.
Example 2:
Input: nums = [1,32,1,2], target = 12 Output: 2 Explanation: In the first operation, we choose element nums[1]. The array becomes equal to nums = [1,1,2,16,16]. In the second operation, we choose element nums[3]. The array becomes equal to nums = [1,1,2,16,8,8] At this stage, nums contains the subsequence [1,1,2,8] which sums up to 12. It can be shown that there is no shorter sequence of operations that results in a subsequence that sums up to 12.
Example 3:
Input: nums = [1,32,1], target = 35 Output: -1 Explanation: It can be shown that no sequence of operations results in a subsequence that sums up to 35.
Constraints:
1 <= nums.length <= 1000
1 <= nums[i] <= 230
nums
consists only of non-negative powers of two.1 <= target < 231
代码结果
运行时间: 31 ms, 内存: 16.1 MB
/*
* 题目思路:
* 1. 使用 Java Stream API 处理数据。
* 2. 我们依然利用大顶堆 (PriorityQueue) 来管理当前的最大值。
* 3. 通过操作将大于 1 的元素拆分为两个小的元素,直到满足 target 或无法满足。
*/
import java.util.Arrays;
import java.util.PriorityQueue;
public class Solution {
public int minOperations(int[] nums, int target) {
PriorityQueue<Integer> pq = Arrays.stream(nums).boxed()
.collect(Collectors.toCollection(() -> new PriorityQueue<>((a, b) -> b - a)));
long sum = pq.stream().mapToLong(Integer::intValue).sum();
if (sum < target) return -1;
int operations = 0;
while (target > 0) {
int max = pq.poll();
if (sum - max >= target) {
sum -= max;
continue;
}
if (max <= target) {
target -= max;
} else {
pq.offer(max / 2);
pq.offer(max / 2);
operations++;
}
sum -= max;
}
return operations;
}
}
解释
方法:
这道题目要求找到最少的操作次数,使得数组的一个子序列的和等于给定的target。首先,若nums数组中所有元素的和小于target,则直接返回-1,因为无论如何都无法达到target。接下来使用计数器cnt来统计nums中每个元素的出现次数。这里采用贪心的策略来尽量使用大的数。通过位运算来判断target的每个位是否需要当前的2的幂次元素。若需要而当前累积的和(acc)不足以提供该位的元素,则必须从更高位的元素中分裂出所需的更小元素,同时计数操作数。这个过程使用了两个循环:外层循环遍历可能的位数,内层循环处理分裂操作,直到找到可用的更大元素。最后,返回执行的总操作次数。
时间复杂度:
O(30) 或 O(log(target))
空间复杂度:
O(1)
代码细节讲解
🦆
为什么在算法中使用贪心策略来尽量使用大的数,这种策略是否总是能保证最少的操作次数?
▷🦆
在算法中,你是如何确保每次操作分裂的结果都是最优的?是否有可能存在分裂顺序不同导致操作次数更少的情况?
▷🦆
算法中提到的外层循环是基于什么条件结束的?为什么选择`1 << i <= target`作为循环条件?
▷🦆
在内层循环中,为什么直接跳过所有cnt[1 << j]为0的元素,这种跳过是否可能忽略了某些潜在的最优解?
▷