Finding the optimal path in a grid - java

Background
I had an interview today and I was asked the following question.
You are given a grid.
int [][] grid =
{
{0,0,0,2,5},
{0,0,0,1,5},
{0,1,1,1,1},
{2,0,0,0,0}
};
You start from the bottom left off the grid. You can only go up and right. The idea is to get to the TOP right corner. You are to take the path that will get you the most Value.
The output for the above would be 16
My solution
public static int getPathMaxSum(int [][] grid){
int row = grid.length-1;
int col = 0;
int currentSum = grid[row][col];
return getMax(grid,currentSum,row,col);
}
public static int getMax(int [][] grid,int sum,int row,int col){
if((isTopRight(grid,row,col)) || (!isValid(grid,row,col))){
return sum;
}else{
sum = sum + grid[row][col];
return Math.max(getMax(grid,sum,row-1,col),getMax(grid,sum,row,col+1));
}
}
public static boolean isTopRight(int [][] grid, int row, int col){
return row == 0 && col == grid[row].length-1;
}
public static boolean isValid(int [][] grid, int row, int col){
return (row >= 0 && row < grid.length) && (col >= 0 && col < grid[row].length);
}
I am trying to solve this recursively. I figure that i have 2 possible choices to make at every position and i want to get the max of the two choices but for some reason I am not able to get the correct output.
I have two helper functions that check if my position is valid meaning inside the grid and also if i have hit the top right and if i have then i hit my base case.
I would love inputs to see where i went wrong.

You don't need a sum parameter in your method.
I assume you already understand how recursion-topdown approach for this problem.
But again just for the sake of completeness, the basic formula is:
You start with a cell at row, col get its value and then either you look to UP (row-1, col) or RIGHT (row, col+1).
So the result is going to be:
grid[row][col] + Math.max( getMax( row-1, col, grid ), getMax( row, col+1, grid ) )
Base conditions:
a) If it is top right i.e. the destination, you don't need to recurse you just return the value at that cell.
b) If it is an invalid cell like you have written in your isValid method, you need to return Integer.MIN_VALUE because you could have a negative value in your other cells and you want them to be maximum.
So your getMax function needs to be:
public static int getMax(int [][] grid,int row,int col){
if (isTopRight(grid,row,col)) {
return grid[row][col];
} else if (!isValid(grid,row,col)){
return Integer.MIN_VALUE;
} else {
return grid[row][col] + Math.max(getMax(grid,row-1,col),getMax(grid,row,col+1));
}
}
You can see working example here

EDIT: Answer to the edited version of your code
Issues with your new solution:
int currentSum = grid[row][col]; and sum = sum + grid[row][col];
The sum is initialized with the value in the bottom left corner and in the initial call of getMax() the same value is added again. This is not what it should be like. Just start the sum with 0, adding will be done by getMax().
if((isTopRight(grid,row,col)) || (!isValid(grid,row,col))) then return sum;
For invalid positions this will work (see restrictions below my code), but not for the top right corner (since we haven't added the value of the corner yet). Thus pull the two conditions apart and only return directly on invalid positions. On any other position first add the value and then, if you reached the "goal", return the sum. Otherwise return the maximum of "going right" and "going up" (the recursive call is correct now).
Fixing these issues and implementing your example, I derived the following code:
public class Pathfinder {
public static void main(String... args) {
int [][] grid = {
{0,0,0,2,5},
{0,0,0,1,5},
{0,1,1,1,1},
{2,0,0,0,0}
};
System.out.println(getPathMaxSum(grid));
}
public static int getPathMaxSum(int[][] grid) {
int row = grid.length - 1;
int col = 0;
return getMax(grid, 0, row, col);
}
public static int getMax(int[][] grid, int sum, int row, int col) {
if(!isValid(grid, row, col))
return sum;
sum = sum + grid[row][col];
if(isTopRight(grid, row, col))
return sum;
return Math.max(getMax(grid, sum, row - 1, col), getMax(grid, sum, row, col + 1));
}
public static boolean isTopRight(int[][] grid, int row, int col) {
return row == 0 && col == grid[row].length - 1;
}
public static boolean isValid(int[][] grid, int row, int col) {
return (row >= 0 && row < grid.length) && (col >= 0 && col < grid[row].length);
}
}
Note, that this version will work for any grid (as long as the stack is big enough and we are not dealing with too large numbers, e.g. we won't get any integer overflow) if all entries are non-negative. Anyways, a grid with negative entries can be manipulated in such a way, that the best path will be found by this algorithm and the solution can be easily "translated" back to the original grid (just subtract the smallest value from every entry).
ANSWER TO THE ORIGINAL CODE
I see multiple issues with your code:
isValid(grid,row+1,col) and sum1 = grid[row+1][col];
You are trying to add 1 to the row, but you started (correctly) with int row = grid.length-1;. Adding 1 will give you an invalid position, thus the first branch will never be executed. Instead, you will need to subtract 1 from the row to "go up".
sum = sum + Math.max(sum1,sum2);
This changes sum, but you cannot see, in which direction you moved. And directly afterwards ...
getMax(grid,sum,row+1,col); and getMax(grid,sum,row,col+1);
... you do the recursive calls with the new, maximum sum, but from both spots. To get a correct solution, you should call them with the value, their direction represents. Note also, that row+1 needs to be replaced by row-1 here as well.
return sum;
You now return this maximum sum, but completely ignoring the returns of your recursive calls. You should instead compare their returns and return yourself the higher value of both.
Bactracking vs. Dynamic Programming
Your algorithm should work in general and is sufficient for small instances of the problem, but not for bigger ones (since it will make 2 recursive calls for every step and you have 2*(n-1) steps.. resulting in exponential runtime). An alternative approach with quadratic runtime would be to go backwards through the field and choose the best way by looking just one field right or up and adding the value of the current field to the maximum of this. Just start in the top right corner and go left, row by row from right to left.

Related

Min Path Through Matrix using Dynamic Programming

I'm working on assignment now for my comp class and am currently getting the hang of dynamic programming. Our current task is that we are given a matrix of size m x n, where either m or n is guaranteed to at least be 2. This matrix is filled with various values that dictate how many "steps" we may move from said position in any cardinal direction, with the goal being that we start at index (0,0) and have to make our way to index (n-1,m-1), or, better put, the bottom right corner of the matrix, in the smallest amount of trips as possible. If such a path is not possible, we must return -1. Here's an example:
1
2
7
5
6
3
The smallest amount of jumps required to get from the top left to the bottom right is 2, since we start at (0,0), which, with value of 1, means we can move 1 step in any direction. We then go to (0,1), which has a value of 2, which we'll use to "step" two positions down into (2,1), which is the bottom right corner. Overall, this journey took two trips: the one from (0,0) to (0,1), and then another from (0,1) to (2,1). It is worth nothing that at position (0,0), I could have also gone down to (0,1), but that wouldn't have resulted in anything useful.
I'm taking a dynamic programming approach, with a table to store previously calculated values and cut down runtime, with a separate table to let me know if I've calculated that position or not. My code is as follows:
private static int [][] dpArray;
private static int [][] solveState;
private static int rowDest;
private static int colDest;
public static int min_moves(int[][] board) {
rowDest = board.length - 1;
colDest = board[0].length - 1;
solveState = new int[rowDest + 1][colDest + 1];
dpArray = new int[rowDest + 1][colDest + 1];
int ans = minMoveRecur(0,0, board);
if (ans == 100000000) {
return -1;
}
private static int minMoveRecur(int row, int col, int[][] board) {
if ((row == rowDest) && (col == colDest)) {
return 0;
}
if ((row < 0) || ( rowDest < row) || (col < 0) || (colDest < col)) {
return 100000000;
}
if (solveState[row][col] == 1) {
return dpArray[row][col];
}
solveState[row][col] = 1;
int up = row - board[row][col];
int down = row + board[row][col];
int right = col + board[row][col];
int left = col - board[row][col];
int vertBest = Math.min(minMoveRecur(up,col,board),minMoveRecur(down,col,board));
int horizBest = Math.min(minMoveRecur(row,right,board),minMoveRecur(row,left,board));
dpArray[row][col] = 1 + Math.min(vertBest,horizBest);
return dpArray[row][col];
}
I take have a recursive relation that finds which path resulted in the minimum number of jumps, but I have been getting the wrong answer for one of test boards I have, where
int [][]board = {{2},{4},{2},{0},{4},{4},{3},{5},{1},{3}};
is supposed to take 4 jumps { (0,0) -> (2,0) -> (4,0) -> (8,0) -> (9,0) }, but I keep getting 2 jumps. I've debugged a few times, and it appears that the issue has to do with the vertBest and horizBest not getting the correct values from the recursive calls. It seems that vertBest's value is always 0, up until the very end where it is 1, which then is added to the 1 from the initial recursive call's dpArray[row][col] = 1 + Math.min(vertBest,horizBest); to be 2. It doesn't seem to be adding on the +1 from its earlier calls.
Can anyone help shed some light on what's going wrong here?
When you try to go up, in the minMoveRecur(up,col,board) call, solveState[row][col] = 1 however you have not updated dpArray[row][col] yet.
As a result since solveState[row][col] = 1 and dpArray[row][col], 0 is returned (due to the 3rd if condition) and 1 is added to it.

How to calculate RECURSIVELY 2D array column in Java

I am stuck with a homework question for something fairly stupid.
The mission is to find the smallest column sum on a 2D array and return it's index. No loops allowed, only recursion.
I managed the code, but I'm stuck with the simple task of calculating the column itself.
This is the code I wrote so far:
public static int maxSumCol(int[][] a) {
int maxCol=calculateCol(a, 0, 0);
int colIndex=0;
return maxSumCol(a, 0, 0, maxCol, colIndex);
}
private static int maxSumCol(int[][] a, int i, int j, int maxCol, int colIndex) {
if (j<a.length){
int tempCol=calculateCol(a, i, j);
if (tempCol > maxCol)
colIndex=j;
return maxSumCol(a, i, j+1, maxCol, colIndex);
}
return colIndex;
}
And this is the method that I built to calculate the column sum:
private static int calculateCol(int[][] a, int row, int col){
if (row<=a.length-1)
return a[row][col] + calculateCol(a, row+1, col);
return 0;
}
Unfortunately, I receive an ArrayIndexOutOfBoundsException every time I run the code.
I can't figure where my mistake is.
What I can see from your post, there are two problems.
First, when you calculate the sum of the columns you only check if the column index is less than the length of the outer matrix but this is the number of rows, not columns.
if (j<a.length){
int tempCol=calculateCol(a, i, j);
The second is that when you found a column with greater sum than the one you have store previously, you only update the colIndex but not the maxcol variable where you store the actual value of the sum
if (tempCol > maxCol)
colIndex=j;

What is wrong with my method to see if a value is in a 2D array in O(n) time?

I'm trying to write a method that takes a 2D array(arranged so that the elements in every row are in increasing order from left to right, and the elements in every column are in increasing order from top to bottom) and an int, and sees if the int is in the 2D array. I wanted to use nested loops, but that would make it go in O(N^2) time. I'm therefore trying to make conditionals that make it so it tests if the int is smaller than the first in one of the sub arrays and bigger than the last, and if so, goes onto the next subarray. Here's what I have:
static boolean has(int number, int[][] a) {
int q = 0;
boolean c = false;
for (int i = 0; i < a[q].length-1; i++){
if ((number < a[i][q]) || (number > a[a[j].length-1][i])){
q++;
}
else if (number == a[i][q]){
c = true;
break;
}
else c = false;
}
return c;
}
could use some help. This method compiles but gives me outOfBounds Thanks!
This solution runs in O(n+m):
static boolean has(int number, int[][] a) {
int row = 0;
int col = a[0].length - 1;
while (row < a.length && col >= 0) {
int n = a[row][col];
if (n < number) {
row++;
} else if (n > number) {
col--;
} else {
return true;
}
}
return false;
}
You can solve this in O(log(n) + log(m)) first find the row that contain the integer you're looking for using binary search (since columns are sorted), then find the exact position of the integer in that row, by performing another binary search (since rows are sorted).

recursion cannot get this right

We have triangle made of blocks. The topmost row has 1 block, the next row down has 2 blocks, the next row has 3 blocks, and so on. Compute recursively (no loops or multiplication) the total number of blocks in such a triangle with the given number of rows.
triangle(0) → 0
triangle(1) → 1
triangle(2) → 3
This is my code:
public int triangle(int rows) {
int n = 0;
if (rows == 0) {
return n;
} else {
n = n + rows;
triangle(rows - 1);
}
}
When writing a simple recursive function, it helps to split it into the "base case" (when you stop) and the case when you recurse. Both cases need to return something, but the recursive case is going to call the function again at some point.
public int triangle(int row) {
if (row == 0) {
return 0;
} else {
return row + triangle(row - 1);
}
}
If you look further into recursive definitions, you will find the idea of "tail recursion", which is usually best as it allows certain compiler optimisations that won't overflow the stack. My code example, while simple and correct, is not tail recursive.
You are not making use of the return value of your function. Instead you always declare a new local variable. Otherwise your solution is quite close to the correct one. Also you should add another return in case you are not at row 0.
public static int triangle (int rows) {
int n = 0;
if (rows == 0) {
return n;
} else {
n = n + rows;
n = n + triangle(rows - 1);
}
return n;
}

JAVA - Go game algorithm

I am trying to implement an algorithm to clear dead stones in my Go game.
I hear that floodfill is the best to achieve this as using it recursively would be most effiecient and easier to implement.
I am having trouble using it within my code and was wondering how I should go about implementing it.
This is one of my classes, it is pretty self explanatory.
import java.io.*;
public class GoGame implements Serializable {
int size;
char[][] pos; // This is the array that stores whether a Black (B) or White (W) piece is stored, otherwise its an empty character.
public GoGame(int s){
size = s;
}
public void init() {
pos = new char[size][size];
for (int i=0;i<size;i++) {
for (int j=0;j<size;j++) {
pos[i][j] = ' ';
}
}
}
public void ClearAll() {
for (int i=0;i<size;i++) {
for (int j=0;j<size;j++) {
pos[i][j] = ' ';
}
}
}
public void clear(int x, int y) {
pos[x][y]=' ';
}
public void putB(int x, int y) { //places a black stone on the board+array
pos[x][y]='B';
floodfill(x,y,'B','W');
}
public void putW(int x, int y) { //places a white stone on the board+array
pos[x][y]='W';
floodfill(x,y,'W','B');
}
public char get(int x, int y) {
return pos[x][y];
}
public void floodfill(int x, int y, char placed, char liberty){
floodfill(x-1, y, placed, liberty);
floodfill(x+1, y, placed, liberty);
floodfill(x, y-1, placed, liberty);
floodfill(x, y+1, placed, liberty);
}
}
x and y are the coordinates of the square, placed is the character of the stone put down, liberty is the other character
Any help would be amazing!
while the other answers are technically correct, you are also missing a lot more logic related to go. what you need to do is, i think (on a B move):
for each W neighbour of the move:
check that W group to see if it has any liberties (spaces)
remove it if not
flood fill is useful for finding the extent of a group of stones, but your routine needs a lot more than that (i'm simplifying here, and also trying to guess what this routine is used for - see comments below this answer).
given the above, a flood fill that identifies all the stones in a group would be something like this (note that it uses a second array for the fill, because you don't want to be changing pos just to find a group):
public void findGroup(int x, int y, char colour, char[][] mask) {
// if this square is the colour expected and has not been visited before
if (pos[x][y] == colour && mask[x][y] == ' ') {
// save this group member
mask[x][y] = pos[x][y];
// look at the neighbours
findGroup(x+1, y, colour, mask);
findGroup(x-1, y, colour, mask);
findGroup(x, y+1, colour, mask);
findGroup(x, y-1, colour, mask);
}
}
you can call that to identify a single group (and copy it into mask), so it will help you identify the members of a W group that neighbour a B move (for example), but it is only a small part of the total logic you need.
finally, note that if you want to do something with every stone in a group you have two options. you can call a routine like the one above, and then loop over mask to find the group, or you can put the action you want to do directly inside the routine (in which case you still use mask to control the extent of the flood fill in the test && mask[x][y] == ' ' but you don't use it as a result - all the work is done by the time the routine returns).
(programming something to handle go correctly, following all the rules, is actually quite complex - you've got a lot of work ahead... :o)
I'd use false proof for that. Here is how I find captured stones:
private static final int SIZE = 8;
private static final int VACANT = 0; //empty point
private static final int MY_COLOR = 1; //Black
private static final int ENEMY_COLOR = 2; //White
private static final int CHECKED = 50; //Mark for processed points
private static final int OUT = 100; //points out of the board
private static boolean isCaptured(int col, int row, int[][] board) {
boolean result = !isNotCaptured(col, row, board);
cleanBoard(board);
return result;
}
private static boolean isNotCaptured(int col, int row, int[][] board) {
int value = board[col][row];
if (!(value == MY_COLOR || value == CHECKED))
return true;
int top = row < SIZE - 1 ? board[col][row + 1] : OUT;
int bottom = row > 0 - 1 ? board[col][row - 1] : OUT;
int left = col > 0 ? board[col - 1][row] : OUT;
int right = col < SIZE - 1 ? board[col + 1][row] : OUT;
if (top == VACANT || right == VACANT || left == VACANT || bottom == VACANT)
return true;
board[col][row] = CHECKED;
return (top == MY_COLOR && isNotCaptured(col, row + 1, board))
|| (bottom == MY_COLOR && isNotCaptured(col, row - 1, board))
|| (left == MY_COLOR && isNotCaptured(col - 1, row, board))
|| (right == MY_COLOR && isNotCaptured(col + 1, row, board));
}
private static void cleanBoard(int[][] board) {
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (board[i][j] == CHECKED)
board[i][j] = MY_COLOR;
}
}
}
Then you can call method like this:
isCaptured(5, 4, board)
I think that BFS will be better for this case because you need to explore the neighbors first, so that if any of them is captured then the point is captured.
As others pointed out, there is also a "ko rule" in Go which roughly means that you are not allowed to capture back immediately when a single stone is captured (simplified). In summary, you may want to use an existing library for this.
I recommend the brugo repository, which is available in maven.
<!-- https://mvnrepository.com/artifact/be.brugo/brugo -->
<dependency>
<groupId>be.brugo</groupId>
<artifactId>brugo</artifactId>
<version>0.1.0</version>
</dependency>
It roughly works like this.
(warning: code not tested)
// create a starting position
Position position = new Position(boardSize, komi);
// play a move
Intersection whereToPlay = Intersection.valueOf(4,4);
IntStatus colorToPlay = IntStatus.BLACK;
Position position2 = position.play(whereToPlay, colorToPlay);
// watch the result.
IntStatus[][] matrix = position2.getMatrix()
It also contains objects to export to Load/Save SGF. The loading of SGF files does not only support UTF-8 but also Asian encodings. Here is a screenshot that shows how difficult this is to implement yourself:
If you also plan to use javafx, then run this demo: brugo.go.ui.javafx.goban.GobanComponentDemo
Enough to get you started.

Categories