【问题标题】:Find the maximum result after collapsing an array with subtractions用减法折叠数组后找到最大结果
【发布时间】:2020-03-30 00:49:24
【问题描述】:

给定一个整数数组,我需要通过重复用它们的差替换任意两个数字来将其减少为一个数字,以产生最大可能的结果。

Example1 - 如果我有 [0,-1,-1,-1] 数组然后执行 (0-(-1)) 然后 (1-(-1)) 然后 (2-(-1) ) 将给出 3 作为最大可能输出

Example2- [3,2,1,1] 我们可以得到最大输出 5 { first (1-1) then (0-2) then (3-(-2)}

谁能告诉我如何解决这个问题?

【问题讨论】:

  • 我不明白你描述的指令或算法。 “我有一个数组,我们需要通过从中减去任何 2 个数字来将其减少为一个数字,最终结果应该是最大值”不清楚,示例似乎没有使用相同的算法来获得 3 的结果.
  • @JanChristophTerasa 我还有几个例子:[14, 8, -4, -5] 然后(-5) - (8) = (-13) 所以[14, -4, -13] 然后(14) - (-13) = 27 所以[27, -4] 相差31。这与可能的最小值相反最终值,即(-5) - (14) = (-19) 所以[8, -4, -19] 然后(8) - (-4) = (12) 所以[12, -19] 相差-31。其他可能的最终值包括 15(用于从左到右相减)、-23(用于从右到左相减)、( 8 - (-4) ) - ( (-5) - 14 ) = 7 等。
  • 谢谢,我现在明白了。
  • 在第二个示例中,我得到的最大结果为 5:1-1 = 0 然后0-2 = -2 然后3-(-2) = 5。我是不是误会了什么?
  • 是的,你是正确的。对不起,这是我的错

标签: python arrays algorithm


【解决方案1】:

其他答案都很好,但这里有另一种思考方式:

如果将结果扩展为单个项,您希望所有正数都以加法项结尾,所有负数都以减法项结尾。

如果你有两个标志,那么这很容易:

  1. 从一个负数中减去除一个正数之外的所有正数
  2. 从剩余的正数中减去所有的负数

如果所有数字的符号相同,则在上述过程中选择绝对值最小的一个,将其视为符号相反。这样可以:

  1. 如果只有负数,则从最小的负数中减去它们;或
  2. 如果您只有正数,则从最小的数中减去除一个以外的所有数,然后从剩余的数中减去结果。

【讨论】:

    【解决方案2】:

    考虑一个“锚”元素,我们选择将任意数量的减法串起来。

    那么清楚:

    (Anchor1 - neg1 - neg2 - neg3...) - (Anchor2 - pos1 - pos2 - pos3...)
    

    如果

    是最优的
    Anchor1 = max(array) and Anchor2 = min(array)
    

    通过将其与蛮力以及 Damien 的 great method 进行比较来演示该方法的 JavaScript 代码(可能无法正确处理具有所有相同元素的数组):

    function bruteForce(A){
      if (A.length == 1)
        return A[0]
      let best = -Infinity
      for (let i=1; i<A.length; i++){
        for (let j=0; j<i; j++){
          let A1 = A.slice()
          let A2 = A.slice()
          A1[i] -= A1[j]
          A1.splice(j, 1)
          A2[j] -= A2[i]
          A2.splice(i, 1)
          best = Math.max(
            best, bruteForce(A1), bruteForce(A2))
        }
      }
      return best
    }
    
    function f(A){
      let max = [-Infinity, -1]
      let min = [Infinity, -1]
      
      A.map(function(x, i){
        if (x > max[0])
          max = [x, i]
        if (x < min[0])
          min = [x, i]
      })
      
      let restPos = 0
      let restNeg = 0
      
      A.map(function(x, i){
        if (i != max[1] && i != min[1]){
          if (x >= 0)
            restPos += x
          else
            restNeg += x
        }
      })
      
      return (max[0] - restNeg) - (min[0] - restPos)
    }
    
    function damien(A){
      let absSum = 0
      let minAbs = Infinity
      let allSignsEqual = true
      let firstSign = A[0] > 0
      
      A.map(function(x, i){
        absSum += Math.abs(x)
        minAbs = Math.min(minAbs, Math.abs(x))
        if ((x > 0) != firstSign)
          allSignsEqual = false
      })
      
      return allSignsEqual ? absSum - 2*minAbs : absSum
    }
    
    var n = 6
    var m = 10
    
    for (let i=0; i<100; i++){
      let A = []
      for (let j=0; j<n; j++)
        A.push(~~(Math.random()*m) * [1,1][~~(Math.random()*2)])
      let a = bruteForce(A)
      let b = f(A)
      let c = damien(A)
      if (a != b || a != c)
        console.log(`Mismatch! ${a}, ${b}, ${c} :: ${A}`)
    }
    
    console.log("Done")

    【讨论】:

    • 确实很有趣。你得到的结果和我的提议一样吗?
    • 两种不同的方法,两种不同的排序操作算法,以及相同的简单结果(+1)
    【解决方案3】:

    目标是在一个数组中迭代两个数相减后找到最大的结果。

    问题主要出在算法层面。
    很明显,最终结果以绝对值之和为界:

    Bound = sum_i abs(a[i])
    

    根据x - y的规则,标志可能会经常变化。该算法必须专注于最大化绝对剩余值的总和。

    关键是要考虑当我们决定配对两个数字时会发生什么。如果数字的符号不同,结果将有一个最大绝对值,我们可以决定符号。例如,如果这两个值是 (-5, 10),我们将得到15-15。将数字与不同的符号相关联,我们只需要选择最终符号,使其与另一个数字的符号不同,例如相邻的数字。

    结果是,如果不是所有的符号都相等,我们可以管理结果等于界限。例如:

    1 2 -3 4 5 -6 7 -> pair 2 and -3
    1 -5 4 5 -6 7   -> pair 1 and -5
    -6 4 5 -6 7
    -10 5 -6 7
    15 -6 7
    -21 7
    28 = Bound
    

    如果所有符号都相等,则无法达到界限。但是,可以通过选择具有最低绝对值的第一个数字来最小化损失。例如:

     3 4 1 2 2  -> pair 1 and 2
     3 4 -1 2   -> pair -1 and 2
     3 4 -3
     3 -7
     10 = Bound - 2*1
    

    如果所有符号都是负数,则可以使用相同的过程。关键是在第一次“操作”之后,我们得到了一个新的界限,这可以通过“符号规则”来实现。

    在这种情况下(所有符号相等),结果等于边界减去最小绝对值的两倍。

    当然,必须单独处理n = 1 案例:result = a[0];

    由此,编写程序相当容易。

    【讨论】:

    • 将您的方法添加到我的代码中,与蛮力进行比较。
    猜你喜欢
    • 1970-01-01
    • 2015-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-10
    • 2014-11-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多