最小长度子序列,正和<= K.

问题描述 投票:2回答:3

相反的问题:

具有正和<= K的最大长度子序列实际上是标准的01背包问题。

它的解决方案非常简单:

int solve(const vector<int> &A, int K) {
    int dp[A.size()+1][K+1];
    int i, j;

    // Base Cases
    for(i=0; i<= K; i++)
        dp[0][i] = 0;
    for(i=0; i<= A.size(); i++)
        dp[i][0] = 0;


    for(i=1; i <= A.size(); i++)
    {
        for(j=1; j<= K; j++)
        {
            dp[i][j] = dp[i-1][j];
            if(A[i-1] <= j)
                dp[i][j] = max(dp[i][j], 1 + dp[i-1][j-A[i-1]]);
        }
    }
    return dp[A.size()][K]

我很难想到如何将sum <= K的最小长度子序列沿着相同的线实现。

例:

A = [14, 10, 4]
K = 14
Minimum Length Subsequence = 14
Maximum Length Subsequence = 10, 4 (Arrived from Knapsack)

它肯定不像将max更改为min那么容易,因为答案应始终是基本情况。这让我想到,我们需要调整基础案例吗?我被困在这里,需要一些推动力。

关于如何解决这个问题的任何想法?

algorithm dynamic-programming knapsack-problem subsequence minimum-size
3个回答
1
投票

用有序对(sum, length)替换总和。现在应用您知道的先前算法。顺序是词典,按总和然后按长度。你正试图接近(target_sum, 0)

现在最接近的“和”将是最短的子序列,具有最小的正差。


1
投票

下面的代码片段显示了筛网的含义。这是一个简单的解决方案,可能对大输入无用。它不像筛子那样找到素数,它只包含真或假,但更像是组合字典和它们的总和,例如:

{value: 14, components: [4, 10]}  

如果您不熟悉Javascript,则数组的行为更像是关联数组或带字符串键的字典(这就是需要进行Number转换的原因),而for in只会迭代具有值的元素(如果数组是稀疏的)。此外,sliceconcat创建了数组的副本。

function minSub(array, target) {
    var sieve = [[]];
    for (var i in array) {
        var temp = [];
        for (var j in sieve) {
            var val = Number(j) + array[i];
            if (!sieve[val] || sieve[val].length > sieve[j].length + 1) {
                temp[val] = sieve[j].concat([array[i]]);
            }
        }
        for (var j in temp) {
            if (Number(j) <= target) {
                sieve[j] = temp[j].slice();
            }
        }
    }
    var max = 0;
    for (var j in sieve) {
        if (Number(j) > max) {
            max = Number(j);
        }
    }
    return sieve[max];
}

console.log(minSub([4, 10, 14], 14));
console.log(minSub([0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0], 8));

请注意,与我在评论中建议的相反,按降序对输入进行排序并不能保证首先找到形成值的最简单组合;每当遇到筛子中已存在的值时,您必须检查组件的数量;例如输入:

{8, 4, 3, 2, 1}

你会发现这个组合:

{value: 9, components: [4, 3, 2]}

在找到之前:

{value: 9, components: [8, 1]}

1
投票

我认为这与你所寻找的一致。在检查是否可以达到总和时,我们必须比在最大子序列的制定中更加小心。在这个公式中,dp[i][j]是与j相加的最小子序列,考虑到A[i]的元素(所以i不是子序列长度)。

JavaScript代码(仅经过轻微测试):

function solve(A, K) {
  let i,j;

  let dp = new Array(length);

  for (i=0; i<A.length; i++)
    dp[i] = new Array(K + 1);

  // Base Cases
  for(i=0; i<A.length; i++)
    dp[i][0] = 0;

  for (i=0; i<A.length; i++){
    // Exact match
    if (A[i] == K)
      return 1;

    // We can reach this sum with only one element
    if (A[i] < K)
      dp[i][A[i]] = 1;
    
    // There are no previously achieved sums
    if (i == 0)
      continue;
    
    for (j=1; j<=K; j++){
      dp[i][j] = dp[i][j] || dp[i - 1][j];

      if (A[i] <= j){
        dp[i][j] = Math.min(
          dp[i][j] || Infinity,
          1 + (dp[i - 1][j - A[i]] || Infinity)
        );
      }
    }
  }
  
  for (i=K; i>=0; i--)
    if (![undefined, Infinity].includes(dp[A.length - 1][i]))
      return dp[A.length - 1][i];
}

console.log(solve([1,2,3,4,5,6,7,8,9,10], 11));
console.log(solve([14,10,4], 14));
console.log(solve([0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0], 8));
console.log(solve([7,7,2,3],15))
© www.soinside.com 2019 - 2024. All rights reserved.