I am attempting to solve the Minimum Path Sum algorithm problem and I have a working solution, however there was an unwritten requirement that the algorithm not exceed an unspecified amount of time for any given matrix of size (m x n).
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.
I know that this is one of these problems perfectly suited to Dynamic Programming, however my brain turns to mush when I start reading up on it. This is supposed to be possible with two different state arrays capturing sums, but I am not comprehending how to do this accurate such that I am sure I am going the right direction.
public class Solution { int width = 0; int height = 0; HashMap<Point, Integer> previousStates = new HashMap<Point, Integer>(); public int minPathSum(int[][] grid) { previousStates = new HashMap<Point, Integer>(); height = grid.length; width = grid[0].length; return func(grid, new Point(0, 0)); } int func(int[][] grid, Point p) { if (p.x == (width - 1) && p.y == (height - 1)) { return grid[p.y][p.x]; } Point rightMostPoint = new Point(p.x + 1, p.y); Point downMostPoint = new Point(p.x, p.y + 1); boolean existingRightPreviousState = previousStates.containsKey(rightMostPoint); boolean existingDownPreviousState = previousStates.containsKey(downMostPoint); int rightLowestCost = (existingRightPreviousState) ? previousStates.get(rightMostPoint) : Integer.MAX_VALUE; int downLowestCost = (existingDownPreviousState) ? previousStates.get(downMostPoint) : Integer.MAX_VALUE; if (rightMostPoint.x < width && !existingRightPreviousState) { rightLowestCost = func(grid, rightMostPoint) + grid[p.y][p.x]; previousStates.put(rightMostPoint, rightLowestCost); } if (downMostPoint.y < height && !existingDownPreviousState) { downLowestCost = func(grid, downMostPoint) + grid[p.y][p.x]; previousStates.put(downMostPoint, downLowestCost); } return (rightLowestCost <= downLowestCost) ? rightLowestCost : downLowestCost; } } class Point { int x; int y; public Point(int x, int y) { this.x = x; this.y = y; } public boolean equals(Object obj) { if (obj == null || !(obj instanceof Point)) return false; Point p = (Point)obj; return this.x == p.x && this.y == p.y; } public int hashCode() { return this.x ^ this.y; } }
I start at the top left and recursively work either down or right based on which one returns the lowest sum. For a given cell in the matrix if I have already encountered it then I already know which Point
has the lowest cost in relation to that so I don't have to re-evaluate it. In a way I am capturing state and using it but it still doesn't seem to meet the performance requirements of this problem. I am guessing the unspoken requirement is \$O(n)\$ complexity and I am unsure in my recursive approach if that is possible.
Do you have any suggestions for improvement here so that I can reduce the operational complexity of this algorithm or at least offer me a better explanation of how dynamic programming can be used to solve this?