Home > other >  Dynamic programming to find the maximum cost path of a 2D grid
Dynamic programming to find the maximum cost path of a 2D grid

Time:11-12

Given a 2D grid, G with q rows and p columns where each cell G[i][j] contain an integer. It need to travel from the top left (0,0) to bottom right (q-1,p-1). I can only move right and down 1 unit at a time. As I visit a cell, the amount in the cell G[i][j] is added to the total. I need to maximize total and get the path. This is my first attempt

def findMaxPerson1(q, p, grid, memo={}):
    key = f"{q},{p}"
    if key in memo:
        return memo[key]
    if q == 0 and p == 0:  # Start point
        return grid[0][0]
    if q < 0 or p < 0:
        return 0
    up = findMaxPerson1(q-1, p, grid, memo)  # Up
    left = findMaxPerson1(q, p-1, grid, memo)  # Left
    if up >= left:
        memo[key] = up   grid[q][p]
        return memo[key]
    else:
        memo[key] = left   grid[q][p]
        return memo[key]

I got 15 from above code. Now I try to modify the previous code to get also the path and the maximum total. This is my attempt code:

def findMaxPerson1(q, p, grid, memo={}):
        key = f"{q},{p}"
        if key in memo:
            return memo[key]
        if q == 0 and p == 0:
            return {
                'total': grid[q][p],
                'path': [(q, p)]
            }
        if q < 0 or p < 0:
            return None
        up = findMaxPerson1(q-1, p, grid, memo)  # Up
        left = findMaxPerson1(q, p-1, grid, memo)  # Left
        if up is not None and left is not None:  # Both are dictionary
            if up['total'] >= left['total']:
                up['total'] = up['total']   grid[q][p]
                up['path'].append((q, p))
                memo[key] = up
                return up
            else:
                left['total'] = left['total']   grid[q][p]
                left['path'].append((q, p))
                memo[key] = left
                return left
        if up is not None:
            up['total'] = up['total']   grid[q][p]
            up['path'].append((q, p))
            memo[key] = up
            return up
        else:
            left['total'] = left['total']   grid[q][p]
            left['path'].append((q, p))
            memo[key] = left
                return left

    print(findMaxPerson1(2, 1, grid, {}))

Using this modified code with the same grid, I get this output:

{'total': 19, 'path': [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]}

This is the grid I use:

grid = [
    [1, 2],
    [3, 4],
    [5, 6],
]

Why the path is not correct? Does anyone know what am I doing wrong? Thank you in advance.

CodePudding user response:

The problem in the second approach is that you're reusing and updating the same object as the value for multiple keys in memo.

It the first approach, the value of memo is a simple integer, and when you do memo[key] = up grid[q][p] the value is copied. Now the value of up and value of memo[key] are completely independent of each other.

In contrast, in the second approach, you do:

up['total'] = up['total']   grid[q][p]
up['path'].append((q, p))
memo[key] = up

which modifies up without copying it, and now you have the same object in up and memo[key] which has the same updated value.


Naive way to fix the issue would be to deepcopy up and left before the modification, as such:

import copy
...
up = copy.deepcopy(up)
left = copy.deepcopy(left)

This fixes the issue, now second approach prints:

{'total': 15, 'path': [(0, 0), (1, 0), (2, 0), (2, 1)]}

But this is very inefficient, as it copies the whole path every time, making your algorithm O(n4) instead of O(n²).

The better way is to store only the previous key for each key (instead of the whole path). After the main part of the algorithm is done, you can reconstruct the path by following this backward references.

  • Related