【问题标题】:Find a if a possble subsequence sum is possible如果可能的子序列和是可能的,则查找
【发布时间】:2017-01-23 18:39:35
【问题描述】:

给定一个数组A 和一个sum,我想知道是否存在一个长度为K 的子序列,使得子序列中所有元素的总和等于给定的sum

代码:

   for i in(1,N):
    for len  in (i-1,0):
      for sum in (0,Sum of all element)
              Possible[len+1][sum] |= Possible[len][sum-A[i]]

时间复杂度O(N^2.Sum)。有什么办法可以将时间复杂度提高到 O(N.Sum)

【问题讨论】:

  • 这应该在CodeReview
  • 数字是正数吗?
  • @ThisaruGuruge 这不是代码审查我已经提供了O(N*N*Sum) 的工作代码现在我想提高它的时间复杂度,这是一个算法问题
  • @GuillaumeG 是的,所有数字都是 +ve
  • 这就是 codereview 的用途。

标签: algorithm subsequence


【解决方案1】:

我的函数将k 相邻数组项的窗口移动到数组A 中,并保持总和到数据,直到搜索失败为止。

int getSubSequenceStart(int A[], size_t len, int sum, size_t k)
{    
    int sumK = 0;

    assert(len > 0);
    assert(k <= len);

    //  compute sum for first k items
    for (int i = 0; i < k; i++)
    {
        sumK += A[i];
    }

    //  shift k-window upto end of A
    for (int j = k; j < len; j++)
    {
        if (sumK == sum)
        {
            return j - k;
        }

        sumK += A[j] - A[j - k];
    }

    return -1;
}

复杂度与数组A的长度呈线性


非连续一般子数组情况的更新:
要找到一个可能不连续的子数组,您可以通过从A 的每个元素中减去sum/k 并寻找总和为零的子集来将您的问题转换为subset sum problem。众所周知,subset sum problem 的复杂性是指数级的。因此,除非您的数组 A 具有特殊属性,否则您不能指望线性算法。

【讨论】:

  • 这无需任何开销即可达到目标。太棒了!
  • 这会检查是否存在长度为 k 的 sublist,而不是子序列。
  • 子序列和子数组不同
【解决方案2】:

编辑:

这实际上可以在没有队列的情况下在线性时间内解决(允许负数)。

C#代码:

bool SubsequenceExists(int[] a, int k, int sum)
{
    int currentSum = 0;
    if (a.Length < k) return false;

    for (int i = 0; i < a.Length; i++)
    {
        if (i < k)
        {
            currentSum += a[i];
            continue;
        }

        if (currentSum == sum) return true;
        currentSum += a[i] - a[i-k];
    }
    return false;
}

原答案:

假设您可以使用长度为 K 的队列,这样应该可以在线性时间内完成这项工作。

C#代码:

bool SubsequenceExists(int[] a, int k, int sum)
{
    int currentSum = 0;
    var queue = new Queue<int>();

    for (int i = 0; i < a.Length; i++)
    {
        if (i < k)
        {
            queue.Enqueue(a[i]);
            currentSum += a[i];
            continue;
        }

        if (currentSum == sum) return true;
        currentSum -= queue.Dequeue();
        queue.Enqueue(a[i]);
        currentSum += a[i];
    }
    return false;
}

这背后的逻辑非常简单:

  1. 我们使用第一个 K 元素填充队列,同时还将其总和存储在某处。
  2. 如果结果总和不等于 sum,则我们从队列中取出一个元素并从 A 添加下一个元素(同时更新总和)。
  3. 我们重复第 2 步,直到到达序列末尾或找到匹配的子序列。

哒哒!

【讨论】:

    【解决方案3】:

    is_subset_sum(int set[], int n, int sum) 成为函数来查找是否存在set[] 的子集且总和等于总和。 nset[] 中的元素个数。

    is_subset_sum problem 可以分为两个子问题

    • 包括最后一个元素,recur for n = n-1, sum = sum – set[n-1]
    • 排除最后一个元素,在 n = n-1 时重复。

    如果上述任何子问题返回 true,则返回 true。

    以下是 is_subset_sum() 问题的递归公式。

    is_subset_sum(set, n, sum) = is_subset_sum(set, n-1, sum) || is_subset_sum(set, n-1, sum-set[n-1])
    
    Base Cases:
    
    is_subset_sum(set, n, sum) = false, if sum > 0 and n == 0
    
    is_subset_sum(set, n, sum) = true, if sum == 0 
    

    我们可以使用动态规划解决Pseudo-polynomial time 中的问题。我们创建一个布尔二维表子集[][] 并以自下而上的方式填充它。如果 set[0..j-1] 的一个子集的和等于 i.,则子集 [i][j] 的值将为真,否则为假。最后,我们返回子集[sum][n]

    解的时间复杂度是O(sum*n)。

    用 C 实现

    // A Dynamic Programming solution for subset sum problem
    
    #include <stdio.h>
    
    // Returns true if there is a subset of set[] with sun equal to given sum
    
    bool is_subset_sum(int set[], int n, int sum) {
    
        // The value of subset[i][j] will be true if there is a
    
        // subset of set[0..j-1] with sum equal to i
    
        bool subset[sum+1][n+1];
    
        // If sum is 0, then answer is true
    
        for (int i = 0; i <= n; i++)
    
          subset[0][i] = true;
    
        // If sum is not 0 and set is empty, then answer is false
    
        for (int i = 1; i <= sum; i++)
    
          subset[i][0] = false;
    
         // Fill the subset table in botton up manner
    
         for (int i = 1; i <= sum; i++) {
    
           for (int j = 1; j <= n; j++) {
    
             subset[i][j] = subset[i][j-1];
    
             if (i >= set[j-1])
    
               subset[i][j] = subset[i][j] || subset[i - set[j-1]][j-1];
           }
    
         }
    
        /* // uncomment this code to print table
    
         for (int i = 0; i <= sum; i++) {
    
           for (int j = 0; j <= n; j++)
    
              printf ("%4d", subset[i][j]);
    
           printf("\n");
    
         } */
    
         return subset[sum][n];
    }
    
    // Driver program to test above function
    
    int main() {
    
      int set[] = {3, 34, 4, 12, 5, 2};
    
      int sum = 9;
    
      int n = sizeof(set)/sizeof(set[0]);
    
      if (is_subset_sum(set, n, sum) == true)
    
         printf("Found a subset with given sum");
    
      else
    
         printf("No subset with given sum");
    
      return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-14
      • 2012-12-28
      • 1970-01-01
      相关资源
      最近更新 更多