【问题标题】:Maximum sum of path from left column to right column从左列到右列的最大路径总和
【发布时间】:2015-09-27 11:18:45
【问题描述】:

我正在尝试计算在网格中从左列到右列可以实现的最大总和。允许的运动是向上、向下、向右。我已经实现了这个解决方案(它是广度优先搜索):

for(int i=1; i<=n; i++) {
    Queue<Position> q = new LinkedList<Position>();
    q.add(new Position(i, 1));
    dp[i][1] = map[i][1];

    while(!q.isEmpty()) {
        Position node = q.poll();
        visited[node.n][node.m] = 1;
        if(dp[node.n][node.m] > max) {
            max = dp[node.n][node.m];
        }
        if(visited[node.n-1][node.m] != 1 && node.n != 1 && dp[node.n-1][node.m] < dp[node.n][node.m] + map[node.n-1][node.m] && map[node.n-1][node.m] != -1) {
            dp[node.n-1][node.m] = dp[node.n][node.m] + map[node.n-1][node.m];  

            q.add(new Position(node.n-1, node.m));
        }
        if(visited[node.n+1][node.m] != 1 && node.n != n && dp[node.n +1][node.m] < dp[node.n][node.m] + map[node.n+1][node.m] && map[node.n+1][node.m] != -1) {
            dp[node.n +1][node.m] = dp[node.n][node.m] + map[node.n+1][node.m];
            q.add(new Position(node.n + 1, node.m));
        }
        if(visited[node.n][node.m+1] != 1 && node.m != m && dp[node.n][node.m+1] < dp[node.n][node.m] + map[node.n][node.m+1] && map[node.n][node.m+1] != -1) {
            dp[node.n][node.m+1] = dp[node.n][node.m] + map[node.n][node.m+1];
            q.add(new Position(node.n, node.m+1));
        }

    }
}
static class Position {
        int n, m;
        public Position(int row, int column) {
            this.n = row;
            this.m = column;
        }
    }

示例输入:

-1 4 5 1
2 -1 2 4
3 3 -1 3
4 2 1 2

我的解决方案的问题是它应该通过遵循 4->3->3->2 达到 2(在最后一行第 2 列),但我的解决方案将 2 置于已访问状态,因此它不会检查它。如果我删除访问过的数组,它将被困在任何单元格的上、下、上、下无限循环中。

编辑:每个点只能访问一次。

【问题讨论】:

  • 4-&gt;3-&gt;3-&gt;2 没有到达最后一列。你能澄清一下问题陈述吗?
  • 相关:stackoverflow.com/q/32679720/572670 不过好像你有不同的招式。
  • 2 在位置 (4,2),可以通过 4->2 和 4->3->3->2 达到,但我想要一个具有最大点数的,所以它应该达到有第二种选择。问题是在达到 4->2 后,它会将 2 设置为已经访问过,因此 4->3->3 不会考虑将 2 作为下一个位置
  • 另外,如果您允许上、下、右 - 您可以根据需要多次使用循环(如果它是上下循环),并获得“更好”的分数。如果您只寻找简单路径(没有循环),这是最长路径问题,即NP-Hard。
  • 只是为了澄清你允许的动作; “上”和“下”实际上是指“右上”和“右下”,对吗?否则,正如阿米特指出的那样,您可以有无限长的解决方案。

标签: algorithm data-structures graph dynamic-programming


【解决方案1】:

这个问题可以用线性规划方法解决,但有一个小转折,因为你不能多次访问每个单元格,但运动实际上可以带你到那个条件。

为了解决这个问题,你可以注意到在给定的位置(x, y)你要么

  • 刚从(x-1, y) 到达(x, y),因此您可以上、下或右(当然,除非您在边缘)
  • (x, y-1)(即从上方)到达(x, y),然后您只能向下或向右移动
  • (x, y+1)(即从下方)到达(x, y),然后您只能向上或向右移动

这直接转化为以下递归记忆解决方案(代码在 Python 中):

matrix = [[-1, 4, 5, 1],
          [ 2,-1, 2, 4],
          [ 3, 3,-1, 3],
          [ 4, 2, 1, 2]]
rows = len(matrix)
cols = len(matrix[0])
cache = {}

def maxsum(dir, x, y):
    key = (dir, x, y)
    if key in cache: return cache[key]
    base = matrix[y][x]
    if x < cols-1:
        best = base + maxsum("left", x+1, y)
    else:
        best = base
    if dir != "above" and y > 0:
        best = max(best, base + maxsum("below", x, y-1))
    if dir != "below" and y < rows-1:
        best = max(best, base + maxsum("above", x, y+1))
    cache[key] = best
    return best

print(max(maxsum("left", 0, y) for y in range(rows)))

如果不允许跨过负值(即使这样可以保证更大的总和),则更改是微不足道的(如果没有从左列到右列的路径,您需要指定要返回的内容) .

【讨论】:

  • 我尝试添加这两个条件:“如果 dir != "above" and y == 0: best = max(best, maxsum("below",x,rows-1)) if dir != "below" and y == rows-1: best = max(best,maxsum("above",x,0))" 实现从底部到顶部的传送(它会失去传送获得的所有积分)和顶行到底行。但它给出了错误的答案。我做错了吗?
  • @crysis:问题在于,通过环绕式移动,您可以进入一个循环(例如,只需环绕式向下移动),从而从同一个单元格多次传递。处理换行的一种简单方法是将dir 替换为完成的步数(例如,-2 表示 x/y 向下移动两次,0 表示“左”)。请注意,这将使复杂度从 O(rows×cols) 变为 O(rows²×cols),因为空间状态会增加。
  • 避免负单元格(-1 值)的基本情况是什么?我没有得到正确答案
猜你喜欢
  • 2022-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多