【问题标题】:Max sum in an array with constraints具有约束的数组中的最大总和
【发布时间】:2012-10-23 09:09:37
【问题描述】:

我有这个问题,给定一个正数数组,我必须找到元素的最大总和,这样就不会选择两个相邻的元素。最大值必须小于某个给定的 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;
    }

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

【问题讨论】:

  • 2 个元素的最大总和使得没有两个相邻元素被选中? 2个或更多?
  • 在不选取相邻元素的情况下,对数组中所有元素获得的最大总和...

标签: algorithm


【解决方案1】:

我能想到的最好的就是 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] 包含不超过ja[0..i] 的不相邻元素的最大和。解决方案最后是sums[n-1][K]

【讨论】:

  • 非常感谢。我是 DP 新手,这真的很有帮助!
  • +1:另外,如果我错了,请纠正我 - 但我相信子集和问题有所减少:给定子集和的实例,“填充”元素:每 2相邻元素 - 添加一个值 k+1。因此,不可能找到多项式解决方案,并且所提出的伪多项式解决方案可能是最优的。 [当然,除非k &lt; 2^n,然后检查所有组合...]
【解决方案2】:
  1. 制作原始数组 (A1) 的副本 (A2)。
  2. 在数组 (A2) 中查找最大值。
  3. 将前一个邻居之前的所有值和下一个邻居之后的值提取到一个新数组 (A3) 中。
  4. 在新数组 (A3) 中查找最大值。
  5. 检查 sum 是否大于 k。如果 sum 通过检查,您就完成了。
  6. 如果不是,则需要返回复制的数组 (A2),删除第二个较大的值(在步骤 3 中找到)并从步骤 3 重新开始。
  7. 一旦没有可以与最大数字一起使用的数字组合(即在步骤 1 中找到的数字 + 数组中的任何其他数字大于 k),您将其从原始数组 (A1) 中删除并重新开始第 0 步。
  8. 如果由于某种原因没有有效的组合(例如,数组只有三个数字或没有低于 k 的数字组合),则抛出异常,或者如果这看起来更合适,则返回 null。

【讨论】:

  • 第 5 步还没有完成,您必须尽可能接近 K。示例 2 6 2 k=5 你的解是 0 正确的解是 4
  • 我不遵循你的逻辑。如果我检查数组中两个最大数字的总和是否小于 K 并且我确保它们彼此不相邻,那么我就完成了。在 2 6 2 和 k=5 的情况下,它会像这样:6 是最高的,但是新数组中没有值(相邻的数字不包含在新数组中)所以我们最终进入第 7 步。最大编号为 2(步骤 2)。 (第 3 步)在新数组 (A3) 中只有一个 2(第 4 步)。 (step5) 2+2 小于 5 所以返回 sum。
  • @JensSchauder 我假设问题是关于找到两个最大的非相邻数字的两个总和。
【解决方案3】:

第一个想法:蛮力

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

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

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

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

【讨论】:

    【解决方案4】:

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

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

    // 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-18
      • 1970-01-01
      • 2011-09-12
      相关资源
      最近更新 更多