X をゼロに減らすための最小限の操作
整数配列 nums と整数 x が与えられます。1 回の操作で、配列 nums から左端または右端の要素を削除し、その値を x から減算できます。これにより、将来の操作のために配列が変更されることに注意してください。
可能であれば x を正確に 0 に減らすための演算の最小数を返し、それ以外の場合は -1 を返します。
例 1:
入力: nums = [1,1,4,2,3]、x = 5
出力: 2
説明: 最適な解決策は、最後の 2 つの要素を削除して x をゼロに減らすことです。
例 2:
入力: nums = [5,6,7,8,9]、x = 4
出力: -1
例 3:
入力: nums = [3,2,20,1,1,3], x = 10
出力: 5
説明: 最適な解決策は、最後の 3 つの要素と最初の 2 つの要素 (合計 5 つの操作) を削除して x を減らすことです。ゼロに。
制約:
1 <= nums.length <= 105
1 <= nums[i] <= 104
1 <= x <= 109
解決策 1: 推定値、バックサム、および unowned_map を使用します。時間計算量は O(n) である必要があります
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
int n = nums.size();
int res = n + 1;
vector<int> presums(n + 1, 0), backsums(n + 1, 0);
unordered_map<int, int> backMap;
for (int i = 1; i <= n; i++) {
presums[i] = presums[i - 1] + nums[i - 1];
}
for (int i = n; i >= 1; i--) {
backsums[i - 1] = backsums[i] + nums[i - 1];
backMap[backsums[i - 1]] = i - 1;
}
for (int i = 0; i <= n; i++) {
if (presums[i] == x) {
res = min(res, i);
}
if (backMap.find(x - presums[i]) != backMap.end()) {
res = min(res, i + n - backMap[x - presums[i]]);
}
}
return res == n + 1 ? -1 : res;
}
};
解決策 2: 推定、バックサム、および二分探索に基づく。推定の各エントリについて、二分探索を使用してバックサム内の x - presums[i] を見つけます。時間計算量は O(nlogn) である必要があります。
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
int n = nums.size();
int res = n + 1;
vector<int> presums(n + 1, 0), backsums(n + 1, 0);
for (int i = 1; i <= n; i++) {
presums[i] = presums[i - 1] + nums[i - 1];
}
for (int i = n; i >= 1; i--) {
backsums[i - 1] = backsums[i] + nums[i - 1];
}
for (int i = 0; i <= n; i++) {
int target = x - presums[i];
int start = i, end = n;
while (start + 1 < end) {
int mid = start + (end - start) / 2;
if (backsums[mid] > target) {
start = mid;
} else if (backsums[mid] < target) {
end = mid;
} else {
res = min(res, i + n - mid); //(i + 1) + (n - mid)
break;
}
}
if (backsums[start] == target) res = min(res, i + n - start);
if (backsums[end] == target) res = min(res, i + n - end);
}
return res == n + 1 ? -1: res;
}
};