算法方法 - 在网格中找到最佳路径

时间:2012-08-25 23:56:25

标签: algorithm graph-theory dynamic-programming

问题 m x n 网格( 0< = m,n< = 500 )。网格中的每个单元格包含 k 硬币(k可以是负数,也可以是0)。您从 0,0 开始,以 m-1,n-1 结束,您可以向下移动1步或向右移动1步,收集尽可能多的硬币能够。如果 k< 0 ,那个特定的单元格有一个强盗,你不能进入那个单元格。如果您移入 8 相邻单元格中的任何一个,您将被删除 k 硬币。当你到达 m-1,n-1 时,你会有多少硬币?

例如在网格中:

0,23,20,-32
13,14,44,-44
23,19,41,9
46,27,20,0

ans = 129(按照路径:0-13-23-46-27-20-0)

时间限制:5秒

我不认为这个程序可以使用动态编程来解决。我还没有研究过图论(以防它可以用来解决这个问题)。简单的递归方法是我唯一能想到的,在给定的约束下效率太低。

那么解决它的好方法是什么?不要只是发布代码,告诉我如何开始。如果它与图论有关,那么表明涉及哪个定理将是非常有用的。

3 个答案:

答案 0 :(得分:4)

对于longest path problem,您的问题称为weighted directed acyclic graph

当你到达(x,y)时,你可以获得的硬币数量最多:

coins(x,y) = max(coins(x-1,y), coins(x,y-1)) + change

这是recurrence relationship。它可以通过使用递归和memoization来提高性能,也可以使用迭代算法来解决。

迭代算法是一次通过网格一个对角线。从0,0开始。然后计算0,1和1,0。然后是0,2和1,1和2,0。等...

第1步:

 0,  ?,  ?,  ?
 ?,  ?,  ?,  ?
 ?,  ?,  ?,  ?
 ?,  ?,  ?,  ?

第2步:

 0, 23,  ?,  ?
13,  ?,  ?,  ?
 ?,  ?,  ?,  ?
 ?,  ?,  ?,  ?

第3步:

 0, 23,-33,  ?
13, 37,  ?,  ?   // 37 because of max(23,13) + 14
36,  ?,  ?,  ?
 ?,  ?,  ?,  ?

等...

完成此过程后,答案就是右下角的数字。

答案 1 :(得分:4)

  

我认为这个程序不能用动态编程来解决。

为什么不呢?这是动态编程方法的主要候选者。

  

直接的递归方法是我唯一能想到的,在给定的约束下效率太低。

你能建立一个解决5x5网格的递归解决方案吗?完善!从那开始,然后通过为已经解决的单元格添加MxN最佳结果数组来memoize。使用所有大的负值启动该数组,然后在找到更好的解决方案时更新它。已经存在的东西了。完成单元格后,将解决方案放入MxN数组中:下次以递归方式到达时,检查数组是否有数字,如果有值,则返回它而不继续递归

备忘录解决方案本身非常简单。算法的预处理步骤(从相邻单元格中减去负数)需要更多代码。

int solve(int r, int c) {
    if(memo[r][c] != MIN) {
        return memo[r][c];
    }
    int res = grid[r][c];
    int a = 0, b = 0;
    if (r+1 != R) {
        a = solve(r+1, c);
    }
    if (c+1 != C) {
        b = solve(r, c+1);
    }
    res = max(res+a, res+b);
    return memo[r][c] = res;
}

以下是ideone上的解决方案,它按预期返回129

答案 2 :(得分:0)

您的问题可以描述为max-flow problem,因此可由Ford-Fulkerson-Algorithm解决。

转型如下:

  • 删除负数节点并从相邻单元格中减去它们的数量。
  • 0/0将是源,m-1,n-1是接收器
  • 每个节点都连接到下方和右侧的节点,弧的容量等于其目标节点的值。
  • 现在最大流量等于你可以拥有的最大硬币数量

可能有更简单的解决方案,这只是我想到的第一件事。

编辑:正如dasblinkenlight在评论中指出的那样,这不起作用,因为流实际上是多个路径的组合,这当然不是我们想要的。