Hard
题目描述
给你一个整数数组 target ,包含 n 个整数。从一个由 n 个 1 组成的初始数组 arr 开始,你可以执行以下过程:
- 设
x为你当前数组里所有元素的和。 - 选择满足
0 <= i < n的任意下标i,并将arr[i]的值设为x。 - 你可以重复这个过程任意次。
如果能从 arr 构造出 target 数组,请返回 true ;否则,返回 false 。
示例 1:
输入:target = [9,3,5]
输出:true
解释:从 arr = [1, 1, 1] 开始
[1, 1, 1],和为 3,选择下标 1
[1, 3, 1],和为 5,选择下标 2
[1, 3, 5],和为 9,选择下标 0
[9, 3, 5] 完成
示例 2:
输入:target = [1,1,1,2]
输出:false
解释:不可能从 [1,1,1,1] 构造目标数组。
示例 3:
输入:target = [8,5]
输出:true
提示:
n == target.length1 <= n <= 5 * 10^41 <= target[i] <= 10^9
解题思路
解题思路
这道题的关键洞察是:逆向思考。
由于每次操作都会将某个位置的值设为当前数组的总和,而总和是严格递增的,这意味着目标数组中的最大值一定是最后一步生成的。因此,我们可以从目标状态开始,逆向模拟这个过程。
核心思想:
- 最大堆维护:使用最大堆来快速找到当前最大值
- 逆向操作:当前最大值 = 前一步的总和,所以前一步该位置的值 = 当前最大值 - (当前总和 - 当前最大值)
- 终止条件:当所有元素都变为1时成功;如果过程中出现非法值则失败
算法步骤:
- 将所有元素放入最大堆,计算总和
- 取出最大值,计算其他元素的和
- 如果最大值 <= 其他元素和,说明无法构造
- 计算前一步该位置的值,将其放回堆中
- 重复直到所有元素都为1
优化点:
当其他元素和为1时(即只有两个元素且其中一个为1),可以直接通过模运算快速计算,避免超时。
推荐解法是使用最大堆的逆向模拟,时间复杂度相对较优。
代码实现
class Solution {
public:
bool isPossible(vector<int>& target) {
if (target.size() == 1) return target[0] == 1;
priority_queue<long long> pq;
long long total = 0;
for (int x : target) {
pq.push(x);
total += x;
}
while (pq.top() != 1) {
long long largest = pq.top();
pq.pop();
long long rest = total - largest;
if (rest == 1) {
return true;
}
if (rest == 0 || largest <= rest) {
return false;
}
long long prev = largest % rest;
if (prev == 0) prev = rest;
pq.push(prev);
total = rest + prev;
}
return true;
}
};
class Solution:
def isPossible(self, target: List[int]) -> bool:
if len(target) == 1:
return target[0] == 1
import heapq
# Python的heapq是最小堆,所以存储负值来模拟最大堆
heap = [-x for x in target]
heapq.heapify(heap)
total = sum(target)
while -heap[0] != 1:
largest = -heapq.heappop(heap)
rest = total - largest
if rest == 1:
return True
if rest == 0 or largest <= rest:
return False
prev = largest % rest
if prev == 0:
prev = rest
heapq.heappush(heap, -prev)
total = rest + prev
return True
public class Solution {
public bool IsPossible(int[] target) {
if (target.Length == 1) return target[0] == 1;
var pq = new PriorityQueue<long, long>(Comparer<long>.Create((a, b) => b.CompareTo(a)));
long total = 0;
foreach (int x in target) {
pq.Enqueue(x, x);
total += x;
}
while (pq.Peek() != 1) {
long largest = pq.Dequeue();
long rest = total - largest;
if (rest == 1) {
return true;
}
if (rest == 0 || largest <= rest) {
return false;
}
long prev = largest % rest;
if (prev == 0) prev = rest;
pq.Enqueue(prev, prev);
total = rest + prev;
}
return true;
}
}
var isPossible = function(target) {
if (target.length === 1) return target[0] === 1;
let totalSum = target.reduce((sum, num) => sum + num, 0);
let maxHeap = new MaxPriorityQueue();
for (let num of target) {
maxHeap.enqueue(num);
}
while (maxHeap.front().element > 1) {
let largest = maxHeap.dequeue().element;
let restSum = totalSum - largest;
if (restSum === 1) return true;
if (restSum === 0 || largest <= restSum) return false;
let newVal = largest % restSum;
if (newVal === 0) {
if (restSum !== 1) return false;
newVal = restSum;
}
maxHeap.enqueue(newVal);
totalSum = restSum + newVal;
}
return true;
};
复杂度分析
| 复杂度类型 | 分析 |
|---|---|
| 时间复杂度 | O(n log n log M),其中 n 是数组长度,M 是最大元素值。每次操作需要 O(log n) 的堆操作,最多需要 O(log M) 次迭代 |
| 空间复杂度 | O(n),用于存储堆 |