1

Imagine a robot sitting on the upper left hand corner of an NxN grid. The robot can only move in two directions: right and down. Imagine certain squares are “off limits”, such that the robot can not step on them. Design an algorithm to get all possible paths for the robot.

Here is the reference implementation I got, I think the implementation is wrong since it only find one path, other than all possible paths (more details, in line 10, the robot only goes down if no valid path in right. But to find all possible paths, the robot should try both right and down)? Want to confirm my understanding is correct.

ArrayList<Point> current_path = new ArrayList<Point>();
public static boolean getPaths(int x, int y) {
      Point p = new Point(x, y);
      current_path.add(p);
      if (0 == x && 0 == y) return true; // current_path
      boolean success = false;
      if (x >= 1 && is_free(x - 1, y)) { // Try right
        success = getPaths(x - 1, y); // Free! Go right
      }
      if (!success && y >= 1 && is_free(x, y - 1)) { // Try down
        success = getPaths(x, y - 1); // Free! Go down
      }
      if (!success) {
        current_path.remove(p); // Wrong way!
      }
      return success;
}

thanks in advance, Lin

İlker Korkut
  • 3,129
  • 3
  • 30
  • 51
Lin Ma
  • 9,739
  • 32
  • 105
  • 175
  • 1
    that's not an if-else-clause, but two if-statements. the robot will first go to the right, if the path is free, and afterwards go down, if the path is free –  Oct 06 '15 at 08:48
  • 2
    Are you trying to find all _paths_, or are you trying to determine whether a path _exists_ on which the robot can travel? Your current code is not even close to handling the former problem. – Tim Biegeleisen Oct 06 '15 at 08:49
  • @Paul, if right returns success, it will never down down (even if going down is valid path as well). Please feel free to correct me if I am wrong. – Lin Ma Oct 06 '15 at 08:51
  • @TimBiegeleisen, I am trying to find all possible paths. Your advice is appreciated. – Lin Ma Oct 06 '15 at 08:51
  • 1
    Also, finding all possible paths is not even a feasible problem. (Even for a matrix of 10x10). Also, how do you define 2 *different* paths. – vish4071 Oct 06 '15 at 08:51
  • @vish4071, why not? We can track all points (x,y) traveled as long as the points with sequence on the path are unique? Please feel free to correct me if I am wrong. :) – Lin Ma Oct 06 '15 at 08:52
  • But yes, if you want number of different paths, you can get it using a dp solution – vish4071 Oct 06 '15 at 08:53
  • You could also use recursion here, but in either case you are asking for a complete solution from scratch, coming from an SO user. – Tim Biegeleisen Oct 06 '15 at 08:53
  • 1
    @LinMa if right returns `true`, the code will only update the value of `success` there's no return statement until the complete end of the method and the one in the beginning that is only a break-off condition (btw. why is (0 , 0) considered the bottom-right corner of a matrix in this code). The code won't break off anywhere between. –  Oct 06 '15 at 08:54
  • Does your definition of different paths say that *all points must be unique*, or that *even if one point is different, paths are different* – vish4071 Oct 06 '15 at 08:54
  • if you looking for the number of paths: it's \binom{2n}{n}, that is `2n` choose `n`, assuming no obstacles. – sve Oct 06 '15 at 08:55
  • @svs, it is for a completely open matrix. Here, we have grid points which are not to be stepped. But yes, computational complexity will be of this order: `O(2nCn)`, which is obvs factorial order. – vish4071 Oct 06 '15 at 08:57
  • But what is YOUR QUESTION? – CiaPan Oct 06 '15 at 09:01
  • The problem statement is incomplete. It lacks the definition of the 'path', specifically it does not define where the path can (or must) end. – CiaPan Oct 06 '15 at 09:03
  • The usual way this problem is phrased is to ask whether there _exists_ a path upon which the robot can reach from point A to point B. And both dp and recursion are viable ways to solve it. – Tim Biegeleisen Oct 06 '15 at 09:06
  • Is the code actually 'a reference implementation'...?! The upper-left corner of an array is usually indexed with two zeros. If so, the path should start at `x == 0, y == 0` — but the 'reference implementation' will exit on the first `if()` and leave a single-cell path as a result... – CiaPan Oct 06 '15 at 09:08

2 Answers2

2

Here's what you can do:

public  static class Point {
    int x, y;
    public Point (int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public String toString() {
        return String.format("[%d, %d]", x, y);
    }
}

public static void getPathsRec(int x, int y, Deque<Point> currentPath,
                               List<List<Point>> paths) {
    if (x == 0 && y == 0) {
        List<Point> path = new ArrayList<Point>();
        for (Point p : currentPath)
            path.add(p);
        paths.add(path);
        //System.out.println(currentPath);
        return;
    }

    if (x > 0 && is_free(x-1, y)) {
        currentPath.push(new Point(x-1, y));
        getPathsRec(x-1, y, currentPath, paths);
        currentPath.pop();
    }

    if (y > 0 && is_free(x, y-1)) {
        currentPath.push(new Point(x, y-1));
        getPathsRec(x, y-1, currentPath, paths);
        currentPath.pop();
    }
}

static int n = 2;
public static List<List<Point>> getPaths() {
    List<List<Point>> paths = new ArrayList<List<Point>>();
    Deque<Point> d = new ArrayDeque<Point>();
    d.push(new Point(n-1, n-1));
    getPathsRec(n - 1, n - 1, d, paths);
    //System.out.println(paths);
    return paths;
}

This is a simple backtracking. The idea is to visit the next state recursively but to make sure that after the call the state goes back to it's previous state(like it was before the call). Here this is done with popping the element from the Deque.

Notice that for simplicity you could introduce new class Path which would be something like:

class Path {
    List<Point> points;
}

to make the code more readable. Then getPaths() would return List<Path> which is much nicer.

Also consider redefining getPathsRec to have the signature getPathsRec(Point p, Deque<Point>, List<Path> ), that is having one argument Point instead of having x, y. Having x, y seems redundant considering the fact that you've defined class Point. Again this would make it look better.

sve
  • 4,336
  • 1
  • 19
  • 30
  • Thanks svs, I think for every possible path, we need 2N steps, and wondering if we could track them in a array with size 2N, which is more optimized then queue? Thanks. – Lin Ma Oct 06 '15 at 21:22
  • 1
    @LinMa Yes, we can but I think the performance would be the same. – sve Oct 06 '15 at 21:25
  • Thanks svs, mark as answered. Your implementation and comments are so cool. :) – Lin Ma Oct 06 '15 at 21:47
  • @LinMa I'm always happy to help! – sve Oct 06 '15 at 21:51
1

Your solution is wrong because once the it reach (0 == x && y==0), the success value will always set to true. Hence, it wouldn't go into later if

Below is the sample answer for your problem. It uses backtracking algorithm:

public class test {
    static int n = 3; //substitute your n value here
    static ArrayList<Point> current_path = new ArrayList<Point>();
    static boolean[][] blockedCell = new boolean[n][n];
    public static void FindAllWay(int x, int y)
    {
        if (x <0 || y < 0) return;
        Point p = new Point(x, y);
        current_path.add(p);

        if (0 == x && 0 == y){
            System.out.println(current_path.toString());
            current_path.remove(current_path.size()-1);
            return;
        }

        if ((x > 0) && !blockedCell[x-1][y]) //go right
        {
            blockedCell[x-1][y] = true;
            FindAllWay(x-1, y);
            blockedCell[x-1][y] = false;
        }
        if ((y > 0) &&!blockedCell[x][y-1]) // go down
        {
            blockedCell[x][y-1] = true;
            FindAllWay(x, y-1);
            blockedCell[x][y-1] = false;
        }
        current_path.remove(current_path.size()-1);

    }

    public static void main(String[] args)
    {
        FindAllWay(n-1,n-1);
    }
}
Phoebe
  • 36
  • 5
  • Thanks Phoebe, fully agree with your judgement. :) – Lin Ma Oct 06 '15 at 15:20
  • Hello Phoebe. Thank you for sharing your answer with us. What is the purpose of `current_path.remove(current_path.size()-1);` though? – a_sid Nov 04 '20 at 07:57