查找总和最大的一对非重叠子数组

时间:2019-04-19 14:21:24

标签: arrays algorithm

这是面试题中的问题:

  

给定一个数组,整数K和整数J,数组中的每个元素表示一行中种植的树木,该元素的值是该树上有苹果的数量。 K和J是Karen John想要分别选择的连续树的期望数量。凯伦(Karen)和乔恩(Jon)不允许采摘相同的树木。找出Karen和John可以采摘的最大苹果数。

     

示例:

     

数组:4 5 7 8 3 1

     

K:3

     

J:2

     

Karen采摘的前三棵树,John采摘的第二棵树,Karen采摘的苹果总数为4 + 5 + 7 = 16,John采摘的苹果总数为8 + 3 = 11。如果约翰选择了最后的2棵树,而不是紧随Karen之后的2棵树,那么这加起来就达到了27棵。他会采摘4棵苹果而不是11棵苹果,这是错误的解决方法。

从理论上讲,我有一种方法可以解决这个问题,但是它涉及到测试Karen和John之间每一个苹果的总和,这对于庞大的数据集来说过于复杂。我将如何解决这个问题?

6 个答案:

答案 0 :(得分:1)

f(i)代表直到索引i的最佳选择。然后:

f(i) = max(
  // Karen after John
  kr(i) + jl(i - K),

  // Karen before John
  kl(i) + jr(i + 1)
)

where kr is the best K-length
      window from the right;

      kl is the best K-length
      window from the left;

      similarly for jl, jr

答案 1 :(得分:1)

我首先准备一个包含苹果总数的累加数组。

即[4,5,7,8,3,1]-> [4,4 + 5,4 + 5 + 7,4 + 5 + 7 + 8,4 + 5 + 7 + 8 + 3,4 + 5 + 7 + 8 + 3 + 1]。

这样做的好处是它允许您通过执行一次减法来计算任何子数组的总和。

这可以通过O(n)个运算来计算,方法是保持运行总计并每次添加下一个元素。

接下来用它来计算答案“ f(y)”,“ Karen在位置y采摘的苹果量是多少?”对于每个y值。

然后考虑解决以下问题:“如果约翰从位置x摘下,而凯伦选择不重叠的最佳位置,那么最大的苹果量是多少?”我们可以轻松计算出约翰减去苹果后得到的苹果数量,然后我们需要为Karen添加最好的苹果。对于y的所有合法值,最好是f(y)的最大值。通过准备一个数组g(z)也很容易计算,该数组g(z)对于小于等于z的所有y是f(y)的最大值,而h(z)对于大于y的所有y是f(y)的最大值。等于或等于z。

总体而言,这可以计算出O(n)复杂度和O(n)空间的最优值。

答案 2 :(得分:1)

在面试的压力和匆忙中,对于每棵{em>树木的K 我都会得到最好的J可用。

最终结果将是获得的最佳配对。

这远不是一个好的算法,但它的工作原理是O((N-K)*(N-J))复杂度

let array = [4, 5, 7, 8, 3, 1];
let K = 3;
let J = 2;

// An apple a day keeps the doctor away as long as you aim well
function ILoveApples(arr, k, j)
{
  // total apples gathered
  let total = 0;
  // get each K sets
  for (let i = 0; i + k < arr.length; ++i)
  {
    // get the count of apples for the current K set
    let kApples = arr.slice(i, i + k).reduce((a, c) => a + c);
    // no count for the J set yet
    let jApples = 0;
    
    // get each J sets
    for (let l = 0; l + j < arr.length; ++l)
    {
      // Avoid overlapping of sets
      if (i >= l + j || i + k <= l)
      {
        // get the count of the current J set
        let temp = arr.slice(l, l + j).reduce((a, c) => a + c);
        // Get the best  J set for that current K set
        if (temp > jApples)
          jApples = temp;
        
      }
    }
    //get the total and save it if better than the previous best total
    if (kApples + jApples > total)
    {
      total = kApples + jApples;
    }
  }
  return total;
}

console.log(ILoveApples(array, K, J));

答案 3 :(得分:1)

好,因此可以通过动态编程解决。

  • 首先,我们寻找约翰的苹果。因此,我们需要连续摘取2个苹果。让我们看一下示例:
  

4 5 7 8 3 1

  • 好的,所以我们从左移到右,并在大小2的连续值中跟踪最大可能值。因此,我们的数组看起来像
  

0 9 12 15 15 15

  • 现在,我们从右到左寻找Karen的苹果,因为我们已经有了从左到右的John结果。现在,我们从右到左依次访问3个连续的苹果。

  • 我们从8 3 1开始,约翰在8之前的值为12。因此总和为8 + 3 + 1 + 12 = 24。我们将其记录在max_sum变量中。

  • 我们选择7 8 3John的值为9。因此总和为7 + 8 + 3 + 9 = 27。我们将此记录在max_sum中。

  • 然后我们使用5 7 8,依此类推,并继续对其进行比较并记录在max_sum中。

  • 请注意,您还需要对John的处理数据进行right to left的{​​{1}}和John的{​​{1}}迭代并相应地将值记录在left to right中。

  • 最后打印Karen。时间复杂度:max_sum,空间复杂度为max_sum

实施 :(遵循相同的LeetCode question

O(n)

答案 4 :(得分:1)

其他假设

  • 所有树木的苹果数量均为非负数。
  • 树木数量 n 为≤ J + K
  • 因此,最佳策略始终是分别采用 J K 树;这既是最佳的,也是可能的。
  • 对于两个列表,分别是 A B (最大到最小),最大数量为 A < strong> i + B j - i 为0≤ i j 大于 A i + B < / strong> j - i +1 为0≤ i j + 1.(听起来很正确,但是我不确定是真的,因为我不是一个足够好的数学家来证明它。)

方法

我解决此问题的方法如下:

初始通行证, O(n)

对输入数组进行两次遍历,将每个 J -/ K 个长条树求和。这将产生两个数组jsumksum

幼稚的方法是每次都重新求和,这将花费 O(Jn) / O(Kn)时间。为了获得加分,请保持运行总计,并在进行时仅对两个最终值进行加/减,每次都进行恒定的两次算术运算。

找到最高指数,最好是 O(n),最坏情况是 O(n²)

为此,我将在每个列表上使用惰性选择排序以输出最佳索引。最好的情况是,仅需要第一项时 O(n);最坏的情况是 O(n²)

lazy_insertion_sort(a, b, c)ab[a[i]]进行排序,因此c的前b[a[i]]个值是最大c个值;它可以安全地假设c-1个值已经排序。

魔术,最好是 O(n) O(1)不包括lazy_insertion_sort),最坏是O(n²)

这种算法很难用英语来描述。

num_available = 0
jsum_best_indices = [0 ... jsum.length)
ksum_best_indices = [0 ... jsum.length)
while (num_available < n) {
    num_available += 1
    lazy_insertion_sort(jsum_best_indices, jsum, num_available)
    lazy_insertion_sort(ksum_best_indices, ksum, num_available)
    max = -1
    for jbi in [0 ... num_available) {
        kbi = num_available - jbi - 1
        ji = jsum_best_indices[jbi]
        ki = ksum_best_indices[kbi]

        if (ji < ki && ki - ji > J) ||
           (ki < jbi && jbi - kbi > K) {
            candidate = ksum[ki] + jsum[ji]
            if candidate > max {
                max = candidate
            }
        }
    }
    if (max > -1) {
        return max
    }
}
assert false, "The number of trees is too low."

应该始终提供最佳价值。

演示

function first_pass(l, n) {
  var nsum = new Array(l.length - n); // doesn't actually do anything useful; JavaScript is bad.
  
  var running = 0;
  n -= 1;
  for (var i = 0; i < n; ++i) {
    running += l[i];
  }
  for (var i = 0; i < l.length - n; ++i) {
    running += l[i+n];
    nsum[i] = running;
    running -= l[i];
  }
  return nsum;
}

function lazy_insertion_sort(a, b, c) {
  var i, j;
  c -= 1;
  for (i = j = c; i < a.length; ++i) {
    if (b[a[i]] > b[a[j]]) {
      j = i;
    }
  }
  i = a[c];
  a[c] = a[j];
  a[j] = i;
}

function magic(J, K, jsum, ksum, n) {
  var num_available = 0;
  var jsum_best_indices = jsum.map((x,i)=>i);
  var ksum_best_indices = ksum.map((x,i)=>i);
  while (num_available < n) {
    num_available += 1
    lazy_insertion_sort(jsum_best_indices, jsum, num_available)
    lazy_insertion_sort(ksum_best_indices, ksum, num_available)
    var max = -1;
    for (var jbi = 0; jbi < num_available; jbi += 1) {
      var kbi = num_available - jbi - 1;
      var ji = jsum_best_indices[jbi];
      var ki = ksum_best_indices[kbi];

      if ((ji < ki && ki - ji > J) ||
          (ki < jbi && jbi - kbi > K)) {
        var candidate = ksum[ki] + jsum[ji]
        if (candidate > max) {
          max = candidate;
        }
      }
    }
    if (max > -1) {
        return max;
    }
  }
  throw "The number of trees is too low.";
}

document.getElementById("run").addEventListener("click", function () {
  var J = +document.getElementById("J").value;
  var K = +document.getElementById("K").value;
  var l = eval(document.getElementById("array").value);
  
  var jsum = first_pass(l, J);
  var ksum = first_pass(l, K);
  document.getElementById("output").innerText = magic(J, K, jsum, ksum, l.length);
});
Array: <input id="array" value="[1, 1, 1, 2, 2, 5, 6, 7, 5, 2, 2, 3, 1, 1, 1]"/><br />
J: <input id="J" type="number" value="3" /><br />
K: <input id="K" type="number" value="4" /><br />
<button id="run">Run</button>

Output: <span id="output"></span>

此JavaScript代码应被视为伪代码;我还没有调用JavaScript的精巧之处,以使一切合理地以适当的速度运行。

答案 5 :(得分:0)

在 Python3 中添加我的解决方案

def solution(A, K, L) -> int:
    if K + L > len(A):
        return -1
    
    for i in range(1, len(A)):
        A[i] += A[i - 1]

    result, a_max, b_max = A[K + L - 1], A[K - 1], A[L - 1]
    for i in range(K + L, len(A)):
        a_max = max(a_max, A[i - L] - A[i - K - L])
        b_max = max(b_max, A[i - K] - A[i - K - L])
        result = max(result, a_max + A[i] - A[i - L], b_max + A[i] - A[i - K])
    
    return result