算法难题:球堆积问题

时间:2019-12-20 20:58:03

标签: algorithm dynamic-programming

我正在尝试解决此问题:https://www.urionlinejudge.com.br/judge/en/problems/view/1312

  

XYZ电视频道正在开发新的电视节目,参赛者   必须做出一些选择才能获得奖赏。游戏包括   三角形的球堆,每个球都有一个整数值,例如   以下示例显示。

     

enter image description here

     

参赛者必须选择他要拿的球以及他的球   奖赏是这些球的价值之和。但是,参赛者   仅当他也直接接球时才可以接任何给定的球   顶部。这可能需要使用相同的球   规则。请注意,参赛者可以选择不参加任何比赛,   在这种情况下,奖品为零。

     

电视节目导演担心   参赛者可以为给定筹码量获得的最高奖金。由于他是   你的老板,他不知道如何回答这个问题,他   将任务分配给您。

     

输入

     

每个测试用例用几行描述。第一行   包含一个整数 N ,该整数表示堆栈的行数(1   ≤ N ≤1000)。接下来的 N 行的 i th 包含 i 个整数 B ij   (对于1≤ j i N B ij ≤105 >); B ij 的值是   堆栈的 i 行中的 j th 球(第一行是   最上面的一个,如果是最左边的一个,则在每行中第一个球)。

     

最后一个测试用例后面是包含一个零的行。

     

输出

Sample Input  | Sample Output
4             |   7 
3             |   0
-5 3          |   6
-8 2 -8       |
3 9 -2 7      |
2             |
-2            |
1 -10         |
3             |
1             |
-5 3          |
6 -4 1        |
0             |

我很想知道如何解决这个问题的一两个指针。

使用DP方法似乎可以解决,但我不能完全确定其重复性。两个相邻的球可能有重叠的孩子这一事实使事情变得有些困难。

4 个答案:

答案 0 :(得分:4)

这是DP,但我们要侧身而不是自上而下。让我们将球堆栈向左倾斜一点,这样我们就可以将整个堆栈看成一列列。

 3  3 -8  7
-5  2 -2
-8  9
 3

从这个角度来看,游戏规则变成:如果我们想拿球,我们还需要把球拿到上方,而球直接放在左边。

现在,解决问题。我们将为每个球计算数量S[i, j] -这代表在只考虑前i列的情况下,如果取[i,j]位置的球(第i列顶部的第j个球),我们可以获得的最佳总和。

我声称以下重复存在(在某些合理的初始条件下):

S[i, j] = MAX(S[i-1, j] + C[i, j], S[i, j+1])

其中C[i, j]是第i列中前j个球的总和。

让我们分解一下。我们要计算S[i, j]

  • 我们必须在[i,j]接球。现在,让我们假设这是我们从本专栏文章中获取的最底端的球。
  • 这要求将其上方此列中的所有球都取走,总和(包括[i,j]本身)为C[i, j]
  • 这还要求拿到[i-1,j]处的球(当然,除非我们在最左列)。我们知道,从定义上讲,接球的最佳收益是S[i-1, j]
  • 因此,最好的总和为:S[i-1, j] + C[i, j],或者最左边的列仅为C[i, j]
  • 但是我们可以选择不同的方式,并从此列中获取更多球(如果我们有更多球)。我们需要计算并从S[i-1, j] + C[i, j]S[i-1, j+1] + C[i, j+1]等中取最大值,一直到桩的底部。 稍加归纳,就很容易看出它等于MAX(S[i-1, j] + C[i, j], S[i, j+1])

现在实现应该很明显。我们逐列处理堆栈,在每一列中从上至下计算部分和C[i, j],然后从下至上计算S[i, j]。 最后,只要以我们遇到的S[i, j]的最大值(或0)作为答案即可。

这与球的数量成线性关系,所以O(N ^ 2)。

为说明起见,给定示例为(C[i, j]S[i, j])对。

(  3, 3) ( 3,7) ( -8,-1)  (7,6)
( -2,-2) ( 5,7) (-10,-3) 
(-10,-7) (14,7)
( -7,-7)

答案 1 :(得分:1)

(已根据Worakarn Isaratham的answer进行了更深入的了解更新。)

通过对角线进行迭代,我们可以在O(N^2)个搜索空间中进行一次简单的重复操作(请注意,共有O(N^2)个球,因此为了做得更好,我们无法检查所有条目)。我们说西南。

       \ jth NW diagonal
        x
       x o
      A o x
     x o x o
    x o x B x / ...etc
   C o x o x o / 3rd iteration
  x o D o E F x / 2nd iteration
 x o x o x o x o / 1st iteration (ith SW diagonal)
x o x o x o x G x
           / / / \

沿着西南对角线的每个选择都将限制所有其余的选择,并将总和限制在西北对角线以下(例如,能够选择E意味着我们仅选择了先前迭代中的FG对角线并选择了它会将所有后续选择限制在AE对角线以下。

假设我们将西南对角线标记为i,将西北对角线标记为j,并具有一个函数sum_northwest,该函数在O(1)中进行计算(使用前缀和)并取为参数:一个西北对角线和一个西南边界。然后,如果f(i, j)代表直到第i西南列且边界为j的最优选择:

f(i, j) = max(
  // Skip choosing from
  // this southwest diagonal
  f(i - 1, j),

  // Choose this northwest diagonal
  // on this southwest diagonal
  sum_northwest(j - 1, i) + f(i - 1, j - 1),

  // Choose an earlier northwest diagonal,
  // but then we are obliged to also
  // include this northwest diagonal
  sum_northwest(j - 1, i) + f(i, j - 1)
)

假设我们要对结果进行制表,则时间复杂度为O(|I| * |J|)

JavaScript代码(未优化):

function sum_northwest(M, j, i){
  return M[j].slice(0, i + 1)
    .reduce((a, b) => a + b, 0)
}

function f(M, i, j){
  if (i < 0 || j < 1 || i >= M[M.length-j].length)
    return 0

  let this_northwest =
    sum_northwest(M, M.length - j, i)

  return Math.max(
    f(M, i - 1, j),
    this_northwest + f(M, i - 1, j - 1),
    this_northwest + f(M, i, j - 1)
  )
}

var M = [
  [ 3, 3,-8, 7],
  [-5, 2,-2],
  [-8, 9],
  [ 3]
]

console.log(f(M, 3, 4))

M = [
  [-2,-10],
  [ 1]
]

console.log(f(M, 1, 2))

M = [
  [ 1, 3, 1],
  [-5,-4],
  [ 6]
]

console.log(f(M, 2, 3))

答案 2 :(得分:0)

     O
    /\
   /  \
 A/    \B
 / \  / \
/   \/   \
C    M    D

假设我们在M拿一个球,那么我们还需要从MAOB区域拿走所有球。我们剩下2个三角形:CAM和MBD,我们需要从这两个三角形中选择球以最大化点。这是相同的问题,但输入量较小。 因此,我们在堆栈中所有子三角形的集合上定义了值函数

C[i,j,h] =顶部为(i,j)且高度为{h的子三角形的最大点(我在这里使用直角三角形进行说明,因为这样更容易画

|\
| \
|  \
|   \
| A  \
| |\  \
| | \  \
| |  \  \
| |D  \E \
| |\  |\  \
| | \ | \  \
| |__\|__\  \
| B   M   C  \
|_____________\

A = (i,j) 
B = (i+h, j)
C = (i+h, j+h)
M = (i+h, j+k)
D = (i, j+h-k)
E = (i+k, j+k)

递归公式:

C[i,j,h] = max(
   C[i,j,h-1] // pick no ball at line i+h
   C[i,j+h-k,k] + C[i+k,j+k,h-k] + sum(ADME) for k from 0 to h // if pick a ball at (i+h, j+k)
)

答案 3 :(得分:0)

我将展示一种O(N log N)时间复杂度和O(N)内存复杂度的解决方案。

首先,您是正确的,我们将使用动态编程方法。我们将使用的数据结构将是相同大小的三角形。新三角形中的每个球(如果接过该球)将成为竞争对手的值。我们可以在O(N)时间(从上到下)中构建它。

现在,我们需要注意一些有趣的说法:将采用新三角形中具有最高值的球(除非它为负值,在这种情况下我们将不采取任何行动)。
证明:(非常简单)如果不采用,那么我们可以采用它,并且一定会使总价值更高。

另一个有趣的主张:数据结构中得分最高的球(不包括在下面的所有球)将不被接收。
证明:如果采用了它们,则表示它们的值是正的,没有最高的球,这意味着它们的值更高,这是不可能的。

现在是算法:

Build the data structure.
Set of all taken items = {}
while there are positive elements in the structure:
    Take highest one - put in the set.
    make all below it 0, and put then in the set.
    make all above it negative infinity.
    Rebuild the data structure for the rest.
Return the set

这将始终是最佳选择-我们选择具有最高价值的球,我们永远不会错过任何一个禁令对我们有帮助的球,而且由于重建数据结构,我们也不会错过任何其他球。

内存复杂度很简单:O(N)用于数据结构。
时间复杂度很棘手:创建数据结构是O(N),每次在循环中,我们都会删除至少一半的元素,并且不会为它们重新计算,因此,迭代次数在N中是对数的所以O(N log N)