3

我有这个问题,给定一个正数数组,我必须找到元素的最大总和,这样就不会选择两个相邻的元素。最大值必须小于某个给定的 K。我尝试在没有 k 的情况下考虑类似问题的思路,但到目前为止我失败了。对于后一个问题,我有以下 dp-ish 解决方案

    int sum1,sum2 = 0;
    int sum = sum1 = a[0];

    for(int i=1; i<n; i++)
    {
        sum = max(sum2 + a[i], sum1);
        sum2 = sum1;
        sum1 = sum;
    }

有人可以给我提示如何解决我目前的问题吗?

4

4 回答 4

4

我能想到的最好的就是 O(n*K) dp:

int sums[n][K+1] = {{0}};
int i, j;
for(j = a[0]; j <= K; ++j) {
    sums[0][j] = a[0];
}
if (a[1] > a[0]) {
    for(j = a[0]; j < a[1]; ++j) {
        sums[1][j] = a[0];
    }
    for(j = a[1]; j <= K; ++j) {
        sums[1][j] = a[1];
    }
} else {
    for(j = a[1]; j < a[0]; ++j) {
        sums[1][j] = a[1];
    }
    for(j = a[0]; j <= K; ++j) {
        sums[1][j] = a[0];
    }
}
for(i = 2; i < n; ++i) {
    for(j = 0; j <= K && j < a[i]; ++j) {
        sums[i][j] = max(sums[i-1][j],sums[i-2][j]);
    }
    for(j = a[i]; j <= K; ++j) {
        sums[i][j] = max(sums[i-1][j],a[i] + sums[i-2][j-a[i]]);
    }
}

sums[i][j]a[0..i]包含不超过的非相邻元素的最大和j。解决方案就sums[n-1][K]在最后。

于 2012-04-21T18:35:00.823 回答
0
  1. 制作原始数组 (A1) 的副本 (A2)。
  2. 在数组 (A2) 中查找最大值。
  3. 将它的前一个邻居之前的所有值和它的下一个邻居之后的值提取到一个新数组 (A3) 中。
  4. 在新数组 (A3) 中找到最大值。
  5. 检查总和是否大于 k。如果 sum 通过检查,您就完成了。
  6. 如果不是,您将需要返回复制的数组 (A2),删除第二个大值(在步骤 3 中找到)并从步骤 3 重新开始。
  7. 一旦没有可以与最大数字一起使用的数字组合(即在步骤 1 中找到的数字 + 数组中的任何其他数字大于 k),您将其从原始数组 (A1) 中删除并从步骤 0 重新开始。
  8. 如果由于某种原因没有有效的组合(例如,数组只有三个数字或没有数字组合低于 k),则抛出异常,或者如果这看起来更合适,则返回 null。
于 2012-04-21T18:19:02.540 回答
0

第一个想法:蛮力

迭代所有合法的索引组合并动态构建总和。

当你超过 K 时停止一个序列。

保持序列直到你找到一个更大的序列,它仍然比 K 更小

第二个想法:也许有人可以强迫这成为一种分而治之的事情......

于 2012-04-21T18:44:46.157 回答
0

这是一个没有“k”约束的问题的解决方案,您首先要做的是:https ://stackoverflow.com/a/13022021/1110808

在我看来,上述解决方案可以很容易地扩展为具有 k 约束,只需修改以下 for 循环中的 if 条件以包含约束: possibleMax < k

// Subproblem solutions, DP
for (int i = start; i <= end; i++) {
    int possibleMaxSub1 = maxSum(a, i + 2, end);
    int possibleMaxSub2 = maxSum(a, start, i - 2);

    int possibleMax = possibleMaxSub1 + possibleMaxSub2 + a[i];
    /*
    if (possibleMax > maxSum) {
        maxSum = possibleMax;
    }
    */
    if (possibleMax > maxSum && possibleMax < k) {
        maxSum = possibleMax;
    }
}

正如在原始链接中发布的那样,可以通过添加记忆来改进这种方法,这样就不会重新计算重复子问题的解决方案。或者可以通过使用自下而上的动态编程方法来改进(当前方法是递归自上而下的方法)

您可以在此处参考自下而上的方法:https ://stackoverflow.com/a/4487594/1110808

于 2012-10-23T01:09:37.617 回答