有趣的谜题

时间:2013-10-29 22:19:44

标签: algorithm matrix dynamic-programming

任务定义:

我有一个自然数矩阵。任务是找到从矩阵的左上角到矩阵的右下角的路径并拨打最高分数。 导航规则:如果您位于[i] [j],您可以移动: a)到[i] [j-1],[i] [j + 1],[i + 1] [j]单元格并拨零点 b)到[i + 1] [j + 1]和拨号矩阵[i] [j]分

小例子:

假设您有score 50matrix

0 3 5 3 2
4 7 2 5 2
4 3 5 2 5

假设您在[1] [1]单元格中(矩阵[1] [1] = 7)。您可以导航至:

a) [1][0] cell with 50 score
b) [1][2] cell with 50 score
c) [2][1] cell with 50 score
d) [2][2] cell with 57 score

出了什么问题:

我以非常缓慢的方式解决这个问题...

我尝试在递归的帮助下实现。如果您只想找到最高分,这很容易。像

这样的东西
public int loop(int i, int j) {
  int left = loop(i, j-1);
  int top = loop(i-1, j);
  int diagonal = loop(i-1,j-1) + matrix[i-1][j-1];
  return maximum(left, top, diagonal);
}

但是,我想找到一个得分最高的路径!这是非常耗时的时间。

为什么消耗时间/内存:

还有一个问题:我需要存储路径收集并将其作为参数传递给循环方法。但循环方法在每次迭代时都会分叉,我必须将路径集合复制到迭代次数。否则,每个循环分支将修改公共路径集合,最后我将拥有所有可能的路径。我的意思是如果在lefttop和{ diagonal最大的left我们不得包含与topdiagonal相关联的路径。

问题:

如何以正确的方式解决?

修改

实际上没有必要找到完整的路径。它只需要找到你拨打分数的点(你可以在其中进行对角移动)

2 个答案:

答案 0 :(得分:3)

您不需要动态编程,也不需要蛮力!

要了解原因,让我们分析一下规则:

  • 你可以自由地向左移动j(左右),所以没有理由对这个方向小心 - 你可以随时进入最佳水平位置。
  • 一旦你增加i(向下),就无法回复(虽然你可以在没有获得积分的情况下增加i)。 i的每次增加都应该是最大点数。
  • 你通过离开一个单元获得积分,但你只能留一行。
  • 这意味着您可以细分此问题而不需要动态编程:您可以移动到最佳j位置,然后采取一个对角线步骤;重复直到完成。
  • 最佳i步骤是从具有最高值的行中的非最后一个单元格移动。你不能从最后一个单元格移动,因为没有对角线移动可能 - 所以如果你的矩阵只有一列(或那个行),你永远不会获得积分。您不能丢失积分,因为值是自然数(但如果允许使用负数,您仍然可以跳过一行)。

更详细地说,然后通过......找到最佳路径。

  1. 矩阵只有一列还是一行?反复向右移动而不获得积分然后结束程序。你在这里做不了多少。
  2. 在当前行中找到最大值,忽略最后一个值。
  3. 生成'j'向最大值的单元格移动,然后沿对角线移动。
  4. 如果您不在最后一行,请返回第2步。
  5. 你在最后一排,无法获得更多积分;只需向右下角生成移动即可完成您的路径。
  6. 就是这样!

    请注意,可能存在多个最大路径,您的问题规范并不能保证唯一的解决方案。

    编辑:如果你不需要实际路径,只需要你得分的数字,那么算法就容易多了 - 删除或忽略最后一行和最后一列,然后是每个{{ 1}}(行)返回该行的最大值。

答案 1 :(得分:1)

编辑: 我误读了这个问题,只是向下移动到右边(即:j只能改为jj+1。)所以这个答案是错误的。

您可以使用动态编程来解决此问题。贪婪并不完全有效,因为你只能“向下和向右”旅行。

天真的动态编程解决方案在字面意义上基本上“向后工作”,从右下角开始,并在该单元格开始时计算最大分数。

从左右开始,从下到上,您可以简单地计算出从该分数中获得的最佳分数。您对m x n矩阵执行此操作,然后从左上角开始并选择具有最大分数的方向。