单调路径区域的递归总和

时间:2016-02-12 14:05:01

标签: c recursion path area

我试图用一种递归的方法来找到从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));
}

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

提前致谢

2 个答案:

答案 0 :(得分:1)

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

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

所以如果我理解你想要做的是这个:

  1. 完全按照每个路径行驶一次。
  2. 计算每条不同的路径,并将该路径下的区域添加到总和中。
  3. 以递归方式执行此操作将如下所示:

    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 ,这意味着将区域(x,y)的中间结果保存到表中并查找,而不是每次都计算它。我不会为你写那个解决方案,但这并不难。祝你好运。

答案 1 :(得分:1)

  

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

您的代码未反映出:

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

想想一条完全水平的道路。例如,在2对3网格中,当索引从0开始,左下角为(0 | 0)时,右下角将为(1 | 0)。现在考虑 upper 右上角,即(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)