4

assume that we have a board like this :

enter image description here

and we want to find the most profitable path from left to right with the following movement pattern :

enter image description here

for example in this board the most profitable path is :

enter image description here

i.e. {2, 0} -> {2, 1} -> {3, 2} -> {3, 3}

I wrote the following code :

import java.util.*;

public final class Board {

    private final int[][] board;
    private final int n;

    public Board(int n) {
        board = new int[n][n];
        this.n = n;
        generateBoard();
    }

    public static class Node {

        public int x;
        public int y;
        public int value;

        public Node(int x, int y, int value) {
            this.x = x;
            this.y = y;
            this.value = value;
        }

        @Override
        public String toString() {
            return "{" + x + ", " + y + "}";
        }

    }

    public static class Path implements Comparable<Path> {

        private LinkedList<Node> path = new LinkedList<>();

        public Path() {
        }

        public Path(List<Node> nodes) {
            this.path.addAll(nodes);
        }

        public void addLast(Node node) {
            path.addLast(node);
        }

        public void removeLast() {
            path.removeLast();
        }

        public List<Node> get() {
            return path;
        }

        public int getProfit() {
            return path.stream().map(node -> node.value).mapToInt(value -> value).sum();
        }

        @Override
        public String toString() {
            return path.toString();
        }

        @Override
        public int compareTo(Path o) {
            return getProfit() > o.getProfit() ? 1 : -1;
        }

    }

    public void generateBoard() {
        Random random = new Random();
        for (int x = 0; x < n; x++) {
            for (int y = 0; y < n; y++) {
                board[x][y] = random.nextInt(200) + 1 - 100;
            }
        }
    }

    public void printBoard() {
        for (int[] b : board) {
            System.out.println(Arrays.toString(b));
        }
    }

    public Path findTheMostProfitablePath() {
        TreeSet<Path> paths = new TreeSet<>();
        for (int x = 0; x < n; x++) {
            visit(new Node(x, 0, board[x][0]), paths);
        }
        return paths.last();
    }

    private void visit(Node root, Collection<Path> paths) {
        Stack<Node> stack = new Stack<>();
        stack.add(root);
        Node node;
        Path currentPath = new Path();
        while (!stack.isEmpty()) {
            node = stack.pop();
            currentPath.addLast(node);
            List<Node> children = getChildren(node.x, node.y);
            if (children == null) {
                paths.add(new Path(currentPath.get()));
                currentPath.removeLast();
            } else {
                stack.addAll(children);
            }
        }
    }

    private List<Node> getChildren(int x, int y) {
        if (y == n - 1) {
            return null;
        }
        y++;
        List<Node> children = new LinkedList<>();
        if (x == 0) {
            children.add(new Node(x, y, board[x][y]));
            children.add(new Node(x + 1, y, board[x + 1][y]));
        } else if (x == n - 1) {
            children.add(new Node(x - 1, y, board[x - 1][y]));
            children.add(new Node(x, y, board[x][y]));
        } else {
            children.add(new Node(x - 1, y, board[x - 1][y]));
            children.add(new Node(x, y, board[x][y]));
            children.add(new Node(x + 1, y, board[x + 1][y]));
        }
        return children;
    }

    public static void main(String[] args) {
        Board board = new Board(3);
        System.out.println("Board :");
        board.printBoard();
        System.out.println("\nThe most profitable path :\n" + board.findTheMostProfitablePath());
    }

}

but it fails to find the path... output :

Board :
[-7, 1, 18]
[88, 56, 18]
[-18, -13, 100]

The most profitable path :
[{1, 0}, {2, 1}, {1, 1}, {2, 2}]

whats's wrong with my code?

I know this is not the best algorithm to find the most profitable path and also it is very slow. in a n*n board the number of paths would be :

n * 2 ^ (n-1) < number of paths < n * 3 ^ (n-1)

and in this algorithm we are checking all paths to find the most profitable one.

Thanks.

FaNaJ
  • 1,329
  • 1
  • 16
  • 39
  • A) Algorithm is bad. There is a linear time algorithm (linear in number of squares on the board). B) The problem with *your* algorithm is in the `visit` method. It does some funky stuff that it really shouldn't – Ordous Feb 18 '15 at 14:54

1 Answers1

6

You are solving the Longest Path Problem, which is usually NP-Complete. However, in your case, you actually have a Directed Acyclic Graph, and thus an efficient solution is available with the following recurrence formula:

D(i,-1) = D(i,m) = -INFINITY //out of bounds
D(-1,j) = 0   [  j>=0  ]     //succesful path
D(i,j) = max{ D(i-1, j-1) , D(i-1,j),D(i-1,j+1)} + A[i][j] //choose best out of 3

By applying Dynamic Programming techniques to implement this formula, an efficient O(n*m) optimal solution can be achieved.

When you are done, a simple scan on the rightest column will find you the destination of the "most profitable" path, and you can reconstruct the actual path from there by going back through the above and writing down each decision at each step.

amit
  • 175,853
  • 27
  • 231
  • 333
  • @user3767784 What's not clear for you? Are you familiar with the Dynamic Programming concept? If not - you should read about it, it's a powerful tool. After you are familiar with it - you should be able to understand from this answer how to find an efficient solution. Regarding the problem being NP-Complete for general case, and yours being a DAG - it's a nice to know facts, but it is not crucial to understand the DP solution proposed. – amit Feb 18 '15 at 15:50