Optimize Leaper Graph algorithm? - java

During a 45 minute technical interview with Google, I was asked a Leaper Graph problem.
I wrote working code, but later was declined the job offer because I lacked Data structure knowledge. I'm wondering what I could have done better.
The problem was as following:
"Given an N sized board, and told that a piece can jump i positions horizontally (left or right) and j positions vertically (up or down) (I.e, sort of like a horse in chess), can the leaper reach every spot on the board?"
I wrote the following algorithm. It recursively finds out if every position on the board is reachable by marking all spots on the graph that were visited. If it was not reachable, then at least one field was false and the function would return false.
static boolean reachable(int i, int j, int n) {
boolean grid[][] = new boolean[n][n];
reachableHelper(0, 0, grid, i, j, n - 1);
for (int x = 0; x < n; x++) {
for (int y = 0; y < n; y++) {
if (!grid[x][y]) {
return false;
}
}
}
return true;
}
static void reachableHelper(int x, int y, boolean[][] grid, int i, int j, int max) {
if (x > max || y > max || x < 0 || y < 0 || grid[x][y]) {
return;
}
grid[x][y] = true;
int i2 = i;
int j2 = j;
for (int a = 0; a < 2; a++) {
for (int b = 0; b < 2; b++) {
reachableHelper(x + i2, y + j2, grid, i, j, max);
reachableHelper(x + j2, y + i2, grid, i, j, max);
i2 = -i2;
}
j2 = -j2;
}
}
Now, later it was pointed out that the optimal solution would be to implement Donald Knuth's co-prime implementation:
http://arxiv.org/pdf/math/9411240v1.pdf
Is this something that one should be able to figure out on a 45 minute technical interview??
Besides the above, is there anything I could have done better?
edit:
- I enquired about starting position. I was told starting at 0,0 is fine.
edit2
Based on feedback, I wrote a while-loop with queue approach.
The recursive approach runs into a stack-overflow when n = 85.
However, the while loop with queue method below works up to ~n = 30,000. (after that it runs into heap-issues with memory exceeding GB's). If you know how to optimize further, please let me know.
static boolean isReachableLoop(int i, int j, int n) {
boolean [][] grid = new boolean [n][n];
LinkedList<Point> queue = new LinkedList<Point>();
queue.add(new Point(0,0)); // starting position.
int nodesVisited = 0;
while (queue.size() != 0) {
Point pos = queue.removeFirst();
if (pos.x >= 0 && pos.y >= 0 && pos.x < n && pos.y < n) {
if (!grid[pos.x][pos.y]) {
grid[pos.x][pos.y] = true;
nodesVisited++;
int i2 = i;
int j2 = j;
for (int a = 0; a < 2; a++) {
for (int b = 0; b < 2; b++) {
queue.add(new Point(pos.x+i2, pos.y+j2));
queue.add(new Point(pos.x+j2, pos.y+i2));
i2 = -i2;
}
j2 = -j2;
}
}
}
}
if (nodesVisited == (n * n)) {
return true;
} else {
return false;
}
}

I ask a lot of interview questions like this. I don't think you would be expected to figure out the coprime method during the interview, but I would have docked you for using O(n^2) stack space -- especially since you passed all those parameters to each recursive call instead of using an object.
I would have asked you about that, and expected you to come up with a BFS or DFS using a stack or queue on the heap. If you failed on that, I might have a complaint like "lacked data structure knowledge".
I would also have asked questions to make sure you knew what you were doing when you allocated that 2D array.
If you were really good, I would ask you if you can use the symmetry of the problem to reduce your search space. You really only have to search a J*J-sized grid (assuming J>=i).
It's important to remember that the interviewer isn't just looking at your answer. He's looking at the way you solve problems and what tools you have in your brain that you can bring to bear on a solution.
Edit: thinking about this some more, there are lots of incremental steps on the way to the coprime method that you might also come up with. Nobody will expect that, but it would be impressive!

I'm sorry, I feel like I'm missing something.
If you can only go up or down by i and left or right by j, then a case (x,y) is reachable from a start case (a,b) if there are integers m and n so that
a + m*i = x
b + n*j = y
That is, everything is false for a square board where n > 1.
If you meant more like a knight in chess, and you can go up/down by i and left/right by j OR up/down by j and left/right by i, you can use the same technique. It just becomes 2 equations to solve:
a + m * i + n * j = x
b + o * i + p * j = y
If there are no integers m, n, o and p that satisfy those equations, you can't reach that point.

Related

How to convert this recurrence solution to divide and conquer?

How to solve this question:
Given n balloons, indexed from 0 to n-1. Each balloon is painted with
a number on it represented by array nums. You are asked to burst all
the balloons. If the you burst balloon i you will get nums[left] *
nums[right] coins. Here left and right are adjacent indices of i.
After the burst, the left and right then becomes adjacent.If you burst
the corner balloons then you will get the points that are adjacent to
those balloons.If you burst the last balloon then you will get the
amount of points written on it. Find the maximum coins you can
collect by bursting the balloons wisely.
Sample test case :
{1,2,3,4}
20
{5,7,8}
56
I have tried this solution using recursion which seems to give the correct answer:
public static int maxCoints(List<Integer> list) {
int max = 0;
if (list.size() == 1) {
return list.get(0);
}
if(list.size() == 2) {
return Math.max(list.get(0),list.get(1))*2;
}
for (int i = 0; i < list.size(); i++) {
int left = i == 0 ? 1 : list.get(i-1);
int right = i == list.size()-1 ? 1 : list.get(i+1);
int n = left * right;
List<Integer> tmp = new ArrayList<>(list);
tmp.remove(i);
max = Math.max(max, n + maxCoints(tmp));
}
return max;
}
But I have tried this solution for divide and conquer but it seems to give wrong answer for the first test case this gives answer as 17 instead of 20
int find(vector<int>& v, int L, int R) {
int ans = 0;
// if(L==R) return v[L];
for (int i = L; i <= R; i++) {
int l = find(v, L, i-1);
int r = find(v, i+1, R);
int val = v[L-1]*v[R+1] + l + r;
ans = max(ans, val);
}
return ans;
}
int32_t main() {
fast_io;
ll tt; cin >> tt;
while(tt--) {
ll n; cin >> n;
vector<int> v(n+2,1);
for(int i=1;i<=n;i++) {
cin >> v[i];
}
cout << find(v,1,n) << "\n";
}
return 0;
}
Please help me figure out the mistake.
This appears to be the a minor modification of the burst balloons problem on leetcode which I wrote the editorial solution to.
Recursion will work but for our intents and purposes it is too slow. Recursively removing every balloon and caching gives us 2^N states, which is the power set of our balloons. We'd like to solve this problem in polynomial time.
Divide and conquer is definitely the right idea.
After bursting balloon i, we can divide the problem into the balloons to the left of i (nums[0:i]) and to the right of i (nums[i+1:]).
To find the optimal solution we check every optimal solution after bursting each balloon.
Since we will find the optimal solution for every range in nums, and we burst every balloon in every range to find the optimal solution, we have an O(N^2) ranges times O(N) time per range which is a O(N^3) solution
However, if we try to divide our problem in the order where we burst balloons first, we run into an issue. As balloons burst, the adjacency of other balloons changes. We are unable to keep track of what balloons the endpoints of our intervals are adjacent to. This is where your solution has issues.
To elaborate on that last point:
When you do:
int l = find(v, L, i-1);
You might not actually get the optimal solution. Consider that balloon i - 1 is now adjacent to balloon i + 1 after you've burst balloon i. If you then burst balloon i - 1, balloon i - 2 is now adjacent to balloon i + 1. If you attempt divide on every balloon burst, your find has to somehow still consider balloons outside the range [L, R].
To solve this instead of bursting balloons and dividing we consider adding balloons into an initially empty interval in reverse the order that they were burst.
Let dp(i, j) denote the maximum score on [i, j]. For each balloon k on [i + 1, j - 1], we add it into the interval and compute the score. After we add the balloon we can always then divide the problem into [i, k] and [k, j], because the left and right boundaries are known. This gets rid of adjacency issues.
A trickier part is to fulfill "if you burst the last balloon then you will get the amount of points written on it." We manually iterate over the last balloon we burst and apply divide and conquer as with before.
See the code to get a better idea:
class Solution {
public int maxCoins(int[] nums) {
int n = nums.length + 2;
int[] new_nums = new int[n];
for(int i = 0; i < nums.length; i++){
new_nums[i+1] = nums[i];
}
new_nums[0] = new_nums[n - 1] = 1;
// cache the results of dp
int[][] memo = new int[n][n];
// find the maximum number of coins obtained from adding all balloons from (0, len(nums) - 1)
int ans = 0;
// manually burst the last balloon because it has special rules
for(int i = 1; i < n; ++i){
ans = Math.max(ans, new_nums[i] + dp(memo, new_nums, i, n - 1) + dp(memo, new_nums, 0, i));
}
return ans;
}
public int dp(int[][] memo, int[] nums, int left, int right) {
// no more balloons can be added
if (left + 1 == right) return 0;
// we've already seen this, return from cache
if (memo[left][right] > 0) return memo[left][right];
// add each balloon on the interval and return the maximum score
int ans = 0;
for (int i = left + 1; i < right; ++i)
ans = Math.max(ans, nums[left] * nums[right]
+ dp(memo, nums, left, i) + dp(memo, nums, i, right));
// add to the cache
memo[left][right] = ans;
return ans;
}
}
Input:
[1, 2, 3, 4]
[5, 7, 8]
Output:
20
56

How to avoid stackoverflow error for n = 8 in N Rooks problem

I want to solve N Rooks problem in N x N board using recursion with max N = 8. My code works fine for N = 2, 3, 4, 5, 6, 7. But when N = 8 it gives so many possible results starting with the first row of 1 0 0 0 0 0 0 0 then gives stackoverflow error before checking other possible results starting with the first row of 0 1 0 0 0 0 0 0.
I know about general recursion like fibonacci series, factorial, etc. and I can trace them down. Then I came across a new form of recursion called backtracking recursion. Then I sarted to learn the logic behind this form of recursion and read some pseudocode algorithms. Acually this form of recursion seemed to me a little bit harder to construct than normal recursion.
public class NRooks {
/**
* In this code r = which row, c = which column.
* lastY method just returns column c of last placed rook in
* a given row r in order to remove it.
* row.length, col.length, board.length have no special meaning. They all
* equal to the dimension of board N.
* main() method always initiates first row(r = 0). Therefore in main()
* method r remains 0 and c changes as you can see in putRook(0, i).
* So solve() method always begins from second row(r = 1).
*/
private static int found = 0;
private static int[][] board;
private static int[] row;
private static int[] col;
public static void putRook(int r, int c) {
board[r][c] = 1;
row[r] = 1;
col[c] = 1;
}
public static void removeRook(int r, int c) {
board[r][c] = 0;
row[r] = 0;
col[c] = 0;
}
public static boolean isValid(int r, int c) {
if (row[r] == 0 && col[c] == 0) return true;
return false;
}
public static void showBoard() {
for (int r = 0; r < board.length; r++) {
for (int c = 0; c < board.length; c++) {
System.out.print(board[r][c] + " ");
}
System.out.println();
}
System.out.println();
}
public static int lastY(int r) {
for (int j = 0; j < board.length; j++) {
if (board[r][j] == 1) return j;
}
return -1;
}
public static boolean solve(int r, int c) {
int last;
if (r == 0) return false;
if (r == col.length) {
found++;
/**
* When I dont include below printline statement my code
* works fine until N = 7 then gives SO error.
* But When I include this print statement in order
* to print number of results my code works fine until
* N = 6 then gives SO error
*/
//System.out.println("Found: " + found);
showBoard();
r--;
last = lastY(r);
removeRook(r, last);
c = last + 1;
}
for (int j = c; j < row.length; j++) {
if (isValid(r, j)) {
putRook(r, j);
return solve(r + 1, 0);
}
}
last = lastY(r - 1);
removeRook(r - 1, last);
return solve(r - 1, last + 1);
}
public static void main(String[] args) {
int n = Integer.parseInt(args[0]);
board = new int[n][n];
row = new int[n];
col = new int[n];
for (int i = 0; i < row.length; i++) {
boolean finished; // not important
putRook(0, i);
finished = solve(1, 0);
if (finished) System.out.println("============"); // ignore this too
}
}
}
Stackoverflow points to the lines that contain recursive calls to solve() method.
Note: I know only C like syntax of java and basic data abstraction. I wrote this code with this level of my Java.
I want to solve this problem and N queens problem myself.
Because there are so many solutions to these problems out there, both mathematically and algorithmically. And I am not interested in advanced Java data abstraction things right now.
I only want some advice about my code snippet above something like
Your backtracking algorithm is not efficient. (so straightfoward)
You need to use some Java data abstraction things to solve this problem efficiently.
You need to use another form of recursion like tail recursion (I heard about this too.)
....
The main issue why you're getting Stack Overflow error is the way your recursion is structured. The moment solve is invoked in the main method, it keeps recursing deeper and deeper; in fact, all of its invocations form a single several-thousand-calls-deep chain. For n=7, there are 3193 nested calls (I added a counter to check this). For n=8, it performs about 5k recursive calls before overflowing stack on my machine - I guess stack size is rather small by default.
Thus, to get this to work for higher values of n, you need to restructure your recursion in a way that doesn't perform all the recursive calls as a single chain. I could argue that your current solution isn't really backtracking because it never actually backtracks. Let me illustrate what backtracking means on a simpler problem. Let's say you want to print all binary strings of length n=3 ("000" through "111") programmatically, without relying on knowing the value of n. An implementation for this could be something like this:
def build_binary_string(current_prefix, chars_left):
if chars_left == 0:
print current_prefix
return
build_binary_string(current_prefix + 'a', chars_left - 1)
build_binary_string(current_prefix + 'b', chars_left - 1)
build_binary_string("", 3)
The interesting thing (backtracking!) happens at the moment when build_binary_string is invoked with arguments ("00", 1):
build_binary_string("000", 0) is invoked, prints "000" and returns immediately
we are back into build_binary_string("00", 1) function call, right about to execute build_binary_string(current_prefix + 'b', chars_left - 1)
build_binary_string("001", 0) is invoked, prints "001" and returns immediately
That point when control flow returned from build_binary_string("000", 0) to build_binary_string("00", 1) and it chose to make another function call was backtracking. Note that the depth of recursion never exceeded 3.
I cannot test your code as I do not have some of your methods, but is the int j = c supposed to be int j = r?
for (int j = c; j < row.length; j++) {
if (isValid(row, col, r, j)) {
putRook(b, row, col, r, j);
return solve(b, row, col, r + 1, 0);
}
}
Inside of this line you are passing 0 to c then declaring j=c in the for loop conditions so j < row.length will be true every time. I do not know what your isValid() is though.
return solve(b, row, col, r + 1, 0);
EDIT: I see now the c is being declared in the if block above, but if that if block does not get executed this should be an infinite loop afterward. Maybe check to see if r == col.length is executing correctly.

Creating different power methods

I had some code that calculates powers, by calling square, cube and hypercube methods. Currently the cube method calls the square method in the program, and then the hypercube method calls the cube method. I want to replace the calls to cube and hypercube with calls to the power method, but I'm completely stuck.
Here's the original code which worked.
public int square( int x ){
int i = ( x*x );
return i;
}
public int cube( int x ){
int i = (x * square(x) );
return i;
}
public int hypercube( int x ){
int i = (x * cube(x) );
return i;
}
public int power(int x, int n){
int k;
if (n==2){
k = square(x);
}
else if (n==3){
k = cube(x);
}
else if (n==4){
k = hypercube(x);
}
else if (n==1){
k = x;
}
else {
k = 1;
for (int i = 0; i < n; i++) {
k *= x;
}
}
return k;
}
Now like I said I want to replace the calls in the cube and hypercube methods with calls to the power method, then I still have calls to square, cube etc in the power method. So I want to remove calls to these methods entirely since I no longer need them. Its really bugging me.
This is what I have so far but its giving me StackOverFlowError.
public int square( int x, int n ){
int i = power( x, n );
return i;
}
public int cube( int x, int n ){
int i = power(x , n );
return i;
}
public int hypercube( int x, int n ){
int i = power(x , n );
return i;
}
public int power(int x, int n){
int k;
if (n==2){
k = square(x, n);
}
else if (n==3){
k = cube(x, n);
}
else if (n==4){
k = hypercube(x, n);
}
else if (n==1){
k = x;
}
else {
k = 1;
for (int j = 0; j < n; j++) {
k *= x;
}
}
return k;
}
First off, as you've recognized this is a very bad way of going about things. So anybody else reading this, apart from the usual call to avoid reinventing the wheel (unless it's for educational purposes) don't implement your exponentiation methods this way!
That being said, the reason you're getting a stack overflow (so appropriate) is that you have a circular definition. The simplest way to see this is trying to track down how power works by hand. Let's say I want to run power(3, 2). What happens? Well power(3, 2) recognizes this as an instance of n == 2 and so goes to the square method. However, the square method relies on the power method to get things done and then you repeat ad infinitum. Hence you get function calls piling up on your stack until you run out of space.
P.S. Incidentally, if you are looking at implementing integer exponentiation for personal edification, you might want to look into repeated squaring. It makes your code much faster (a logarithmic number of multiplication operations as opposed to a linear number).
It looks to me that there is an infinite cycle going on here.
If n=2, then power calls sqaure which in turn calls power and n doesn't change either.
You get StackOverFlowError usually when you deal with infinities.
power(x,n) must be independent of cube or square if you want to get rid of those.

Java 2D array error

So I need to take a 2D array do calculations to each elements and transfer that into another 2D array while using the values to the "left" "right" "up" and "down" of the current element. If the current element is on the edge (x = 0, y = 0, x = array.length , y = array.length) I will get an array out of bounds error. I want to create a for loop that deals with each of those cases but I don't know how to do it. A sample of my code is
private void buildE(int[][] array, int y, int x)
{
int up = array[y - 1][x];
int down = array[y + 1][x];
int left = array[y][x - 1];
int right = array[y][x + 1];
if(up == 0){
buildETopRow(array);
}
E will be my new array. This method does not work because y does not equal 0, it just doesn't exist but I can't set ints to null either. In the case of an out of bounds error I need the element (up, down, left, or right) that is out of bounds to equal the current element. Is there a way I can still use a for loop for this or do I need to do something else?
If I read this correctly you want to effectively treat the difference of an element on the edge with an element off the edge as 0. If that's true I would write four methods right(), left(), up() and down(), with down() shown below as an example:
/*
* Return the difference between an element an the element below it
*/
public void down(int x, int y) {
if (y == array.length - 1) {
\\ on the bottom edge
return 0;
}
return array[y][x] - array[y + 1][x];
}
And inside your loop you'd calculate:
up(x,y) + down(x,y) + right(x,y) + left(x,y)
or whatever calculation it is you need to sum up.
The easiest way it to surround your array with a border region. So that your x dimension is really width+2.
import java.util.*;
import java.lang.*;
class Main
{
public static void main (String[] args) throws java.lang.Exception
{
int realWidth = 10;
int realHeight = 10;
int[][] in = new int[(realWidth+2)][(realHeight+2)];
int[][] out = new int[(realWidth+2)][(realHeight+2)];
for (int j = 1;j<realHeight+1;j++)
{
for (int i = 1;i<realWidth+1;i++)
{
int top = in[j-1][i];
int bottom = in[j+1][i];
int left= in[j][i-1];
int right = in[j][i+1];
out[j][i] = operation(top,bottom,left,right);
}
}
}
public static int operation (int top,int bottom,int left,int right)
{
return top+bottom+left+right;
}
}
I'm not totally sure what your question is, but (1) the usual structure for traversing a 2D array is to use nested for loops (one inside the other), and (2) when you want wrap-around counters (e.g. 2, 3, 0, 1, 2, ...) use the remainder operator %.
int numRows = theArray.length;
int numCols = theArray[0].length;
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < numCols; j++) {
int right = theArray[(j+1) % numCols];
int down = theArray[(i+1) % numRows];
int left = theArray[(j+numCols-1) % numCols];
int up = theArray[(i+numRows-1) % numCols];
/* right, down, left, and up will be the elements to the right, down,
left, and up of the current element. Npw that you have them, you can
process them however you like and put them in the other array. */
}
}
What the remainder operator A%B does is sets A back to zero once it gets as large as B. Since B is the size of your array, that's exactly when it is too large and will cause an IndexOutOfBounds error. Note: That's not how % works but it's an ok way to think of what it does. To find out more about it you can google it, I found an ok explanation here.

Java - Maximum sum in path through a 2D array

Basically I have a problem that goes something similar to this:
There is a garden of strawberry plants represented by a 2D, square array. Each plant(each element) has a number of strawberries. You start at the top left corner of the array, and you can only move to the right or down. I need to design a recursive method to calculate the paths through the garden and then output which one yields the most strawberries.
I think I have an understanding of really really simple recursion problems, but this problem has gone way over my head. I'm not really sure where to start or where to go as far as creating a recursive method.
Any help related to the code or helping me understand the concept behind this problem is greatly appreciated. Thanks.
Like dasblinkenlight said, the most efficient way to do this is using a memoization or dynamic programming technique. I tend to prefer dynamic programming, but I'll use pure recursion here.
The answer centers around the answer to one fundamental question: "If I'm in the square in row r and column c on my field, how can I evaluate the path from the top left to here such that the number of strawberries is maximized?"
The key to realize is that there's only two ways to get in the plot in row r and column c: either I can get there from above, using the plot in row r-1 and column c, or I can get there from the side, using the plot in row r and column c-1. After that, you just need to make sure you know your base cases...which means, fundamentally, my purely recursive version would be something like:
int[][] field;
int max(int r, int c) {
//Base case
if (r == 0 && c == 0) {
return field[r][c];
}
//Assuming a positive number of strawberries in each plot, otherwise this needs
//to be negative infinity
int maxTop = -1, maxLeft = -1;
//We can't come from the top if we're in the top row
if (r != 0) {
maxTop = field[r-1][c];
}
//Similarly, we can't come from the left if we're in the left column
if (c != 0) {
maxLeft = field[r][c-1];
}
//Take whichever gives you more and return..
return Math.max(maxTop, maxLeft) + field[r][c];
}
Call max(r-1, c-1) to get your answer. Notice there's a lot of inefficiency here; you'll do much better by using dynamic programming (which I'll provide below) or memoization (which has already been defined). The thing to remember, though, is that both the DP and memoization techniques are simply more efficient ways that come from the recursive principles used here.
DP:
int maxValue(int[][] field) {
int r = field.length;
int c = field[0].length;
int[][] maxValues = new int[r][c];
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
if (i == 0 && j == 0) {
maxValues[i][j] = field[i][j];
} else if (i == 0) {
maxValues[i][j] = maxValues[i][j-1] + field[i][j];
} else if (j == 0) {
maxValues[i][j] = maxValues[i-1][j] + field[i][j];
} else {
maxValues[i][j] = Math.max(maxValues[i][j-1], maxValues[i-1][j]) + field[i][j];
}
}
}
return maxValues[r-1][c-1];
}
In both cases, if you want to recreate the actual path, just keep a 2D table of booleans that corresponds with "Did I come from above or to the left"? If the most strawberry path comes from above, put true, otherwise put false. That can allow you to retrace the patch after the calculation.
Notice that this is still recursive in principal: at each step, we're looking back at our previous results. We just happen to be caching our previous results so we don't waste a bunch of work, and we're attacking the subproblems in an intelligent order so that we can always solve them. For more on dynamic programming, see Wikipedia.
You can do it using memoization. Here is Java-like pseudodoce (memo, R, and C are assumed to be instance variables available to the max method).
int R = 10, C = 20;
int memo[][] = new int[R][C];
for (int r=0 ; r != R ; r++)
for (int c = 0 ; c != C ; c++)
memo[r][c] = -1;
int res = max(0, 0, field);
int max(int r, int c, int[][] field) {
if (memo[r][c] != -1) return memo[r][c];
int down = 0; right = 0;
if (r != R) down = max(r+1, c, field);
if (c != C) right = max(r, c+1, field);
return memo[r][c] = (field[r][c] + Math.max(down, right));
}
You can solve this with DP tabulation method, with which you can save space from O(m*n) to just O(n). With DP Memorization, you need m*n matrix to store intermediate values. Following is my Python code. Hope it can help.
def max_path(field):
dp = [sum(field[0][:i]) for i in range(1, len(field[0]) + 1)]
for i in range(1, len(field)):
for j in range(len(dp)):
dp[j] = min(dp[j], dp[j - 1] if j > 0 else float('inf')) + field[i][j]
return dp[-1]

Categories