📜  总和最接近K的子数组

📅  最后修改于: 2021-05-24 22:38:25             🧑  作者: Mango

给定正负整数数组和整数K。任务是找到总和最接近k的子数组。如果有多个答案,请打印任何一个。
注意:此处最接近表示abs(sum-k)应该最小。

例子:

天真的方法是使用前缀和检查所有可能的子数组和。在这种情况下,复杂度将为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)