【问题标题】:Recursive sum of monotonic path areas单调路径区域的递归总和
【发布时间】:2016-05-23 17:13:18
【问题描述】:

我正在尝试提出一种递归方法来查找从 nxn 网格的左上角到右下角的所有单调路径的所有区域的总和(路径的每一步都可以向右或沿线向下)。

假设左下角有坐标(0,0),我有如下函数:

int sum(int current_sum, int x, int y, int n){
    if (x == n || y == 0)  
        return current_sum;

    return (sum(current_sum+y, x+1, y, n) + sum(current_sum, x, y-1, n));
}

,当它到达网格的右侧或底线时停止(从那里的任何移动都不会改变该区域的当前值)并考虑向右或向下移动产生的区域的总和。结果比它应该的要大,我很难找出原因。有人可以看看吗?

提前致谢

【问题讨论】:

  • 我认为添加一个包含输入、预期和实际输出的具体示例会有很大帮助。
  • 这是来自 Project Euler 的问题:projecteuler.net/problem=15
  • 专注于回报。 x+1,y-1。使用 2 步 for 循环怎么样,x 从低移到高,y 从高移到低?它可以解决向零值墙移动的问题。

标签: c recursion path area


【解决方案1】:

再次阅读,OP 的解决方案似乎已经正确。下面我的回答供参考。


这似乎是 Project Euler problem 15,或者一个非常相似的问题。

所以如果我理解正确你想要做的是这样的:

  1. 每条路径只走一次。
  2. 计算每条不同的路径并将该路径下的面积加到总和中。

以递归方式执行此操作如下所示:

int area(int x, int y)
{
  if (x == 0 || y == 0)
    /* We are at the end of a path, terminate */
    return 0;

  /* We are not at the end, add the two choices possible from here */
  return area(x, y - 1) + area(x - 1, y) + y;
}

你必须画一个图来看看最后一个表达式是否正确。当我们在网格中向右移动(-x)时,我们只将 y 添加到总和中,从而覆盖我们下方的一列。向下移动 (-y) 不会覆盖任何区域。

这个解决方案应该是正确的,但它会很慢。为了加快速度,您可以添加 memoisation 这意味着将 area(x, y) 的中间结果保存到表中并查找它而不是每次都计算它。我不会为您编写该解决方案,但这并不难。祝你好运。

【讨论】:

    【解决方案2】:

    [..] 从 nxn 网格的左上角到 右下角 [..]

    您的代码没有反映:

    // ...
      if (x == n || y == 0)  
        return current_sum;
    // ...
    

    想象一条完全水平的路径。例如,在 2 on 3 网格中,当索引以 0 开头且左下角为 (0 | 0) 时,右下角将为 (1 | 0)。现在考虑右上角,即(1 | 2)。对于这些值,上述条件都不成立,因此您总结了两个下一个单元格的递归调用:(2 | 2)(向右)和(1 | 1)(向下)。

    第一个单元格(向右)是问题所在:x == 2 == n,因此您返回路径的总和尽管它没有在右下角结束。因此,您对太多路径求和,导致总和太大。


    我认为应该这样做:

    unsigned sum_inner(
        unsigned const accumulatedSum,
        size_t const x, size_t const y,
        size_t const gridSideSize) {
      bool atRightEdge = (x == gridSideSize - 1);
      bool atBottomEdge = (y == 0);
      if (atRightEdge && atBottomEdge) {
        // Awesome, in lower right corner, so everything is fine
        // Except that with the implementation of the other two edge cases, this
        // will never be run (except for the 1x1 case)!
        printf("reached lower right edge!\n");
        return accumulatedSum + 1;
      } else if (atRightEdge) {
        // Right edge, so from here one can only go down. Since there's only one
        // possible path left, sum it directly:
        return accumulatedSum + y + 1;
      } else if (atBottomEdge) {
        // Bottom edge, so from here one can only go right. Since there's only one
        // possible path left, sum it directly:
        return accumulatedSum + (gridSideSize - x) + 1;
      } else {
        // Somewhere in the grid, recursion time!
        return sum_inner(accumulatedSum + y, x + 1, y, gridSideSize) +
               sum_inner(accumulatedSum, x, y - 1, gridSideSize);
      }
    }
    
    unsigned sum_monotonic_tl_br(size_t const gridSideSize) {
      return sum_inner(0, 0, gridSideSize - 1, gridSideSize);
    }
    

    (Live with sizes from 1 to 15)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-27
      • 1970-01-01
      相关资源
      最近更新 更多