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?