给定正负整数数组和整数K。任务是找到总和最接近k的子数组。如果有多个答案,请打印任何一个。
注意:此处最接近表示abs(sum-k)应该最小。
例子:
Input: a[] = { -5, 12, -3, 4, -15, 6, 1 }, K = 2
Output: 1
The subarray {-3, 4} or {1} has sum = 1 which is the closest to K.
Input: a[] = { 2, 2, -1, 5, -3, -2 }, K = 7
Output: 6
Here the output can be 6 or 8
The subarray {2, 2, -1, 5} gives sum as 8 which has abs(8-7) = 1 which is same as that of the subarray {2, -1, 5} which has abs(6-7) = 1.
天真的方法是使用前缀和检查所有可能的子数组和。在这种情况下,复杂度将为O(N 2 )。
一个有效的解决方案是使用C++ STL集和二进制搜索来解决以下问题。请按照以下算法解决以上问题。
- 首先将第一个元素插入集合容器中。
- 将答案总和初始化为第一个元素,并将差值初始化为abs(A 0 -k)。
- 迭代从1到N的所有数组元素,并继续在每个步骤中将元素添加为前缀sum到集合容器中。
- 在每次迭代中,由于前缀和已经存在,所以我们只需要从开始就减去一些元素的总和即可得到任何子数组的总和。贪婪的方法将是减去子数组的和,该子数组的和最接近K。
- 使用二进制搜索(可以使用lower_bound()函数)从开头查找与(prefix-k)最接近的子数组之和,因为从前缀和减去该数字将得出最接近K的子数组之和,直到迭代为止。
- 另外,还要检查是否有返回lower_bound()的索引,因为总和可以大于或小于K。
- 如果lower_bound不返回任何此类元素,则如果当前前缀和小于先前计算的和,则将对其进行比较和更新。
下面是上述方法的实现。
// C++ program to find the
// sum of subarray whose sum is
// closest to K
#include
using namespace std;
// Function to find the sum of subarray
// whose sum is closest to K
int closestSubarraySumToK(int a[], int n, int k)
{
// Declare a set
set s;
// initially consider the
// first subarray as the first
// element in the array
int presum = a[0];
// insert
s.insert(a[0]);
// Initially let this difference
// be the minimum
int mini = abs(a[0] - k);
// let this be the sum
// of the subarray
// to be searched initially
int sum = presum;
// iterate for all the array elements
for (int i = 1; i < n; i++) {
// calculate the prefix sum
presum += a[i];
// find the closest subarray
// sum to by using lower_bound
auto it = s.lower_bound(presum - k);
// if it is the first element
// in the set
if (it == s.begin()) {
// get the prefix sum till start
// of the subarray
int diff = *it;
// if the subarray sum is closest to K
// than the previous one
if (abs((presum - diff) - k) < mini) {
// update the minimal difference
mini = abs((presum - diff) - k);
// update the sum
sum = presum - diff;
}
}
// if the difference is
// present in between
else if (it != s.end()) {
// get the prefix sum till start
// of the subarray
int diff = *it;
// if the subarray sum is closest to K
// than the previous one
if (abs((presum - diff) - k) < mini) {
// update the minimal difference
mini = abs((presum - diff) - k);
// update the sum
sum = presum - diff;
}
// also check for the one before that
// since the sum can be greater than
// or less than K also
it--;
// get the prefix sum till start
// of the subarray
diff = *it;
// if the subarray sum is closest to K
// than the previous one
if (abs((presum - diff) - k) < mini) {
// update the minimal difference
mini = abs((presum - diff) - k);
// update the sum
sum = presum - diff;
}
}
// if there exists no such prefix sum
// then the current prefix sum is
// checked and updated
else {
// if the subarray sum is closest to K
// than the previous one
if (abs(presum - k) < mini) {
// update the minimal difference
mini = abs(presum - k);
// update the sum
sum = presum;
}
}
// insert the current prefix sum
s.insert(presum);
}
return sum;
}
// Driver Code
int main()
{
int a[] = { -5, 12, -3, 4, -15, 6, 1 };
int n = sizeof(a) / sizeof(a[0]);
int k = 2;
cout << closestSubarraySumToK(a, n, k);
return 0;
}
时间复杂度: O(N log N)