如何在棋盘中找到最有利可图的路径
How to find the most profitable path in a board
假设我们有这样的板子:
并且我们希望通过以下移动模式从左到右找到最有利可图的路径:
例如,在此版块中,最有利可图的路径是:
即{2, 0} -> {2, 1} -> {3, 2} -> {3, 3}
我写了下面的代码:
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());
}
}
但是找不到路径...
输出:
Board :
[-7, 1, 18]
[88, 56, 18]
[-18, -13, 100]
The most profitable path :
[{1, 0}, {2, 1}, {1, 1}, {2, 2}]
我的代码有什么问题?
我知道这不是找到最赚钱路径的最佳算法,而且速度很慢。
在 n*n
板中,路径数为:
n * 2 ^ (n-1) < number of paths < n * 3 ^ (n-1)
在这个算法中,我们正在检查所有路径以找到最有利可图的路径。
谢谢。
您正在求解 Longest Path Problem, which is usually NP-Complete. However, in your case, you actually have a Directed Acyclic Graph,因此可以使用以下递推公式获得有效的解决方案:
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
通过应用 Dynamic Programming 技术来实现此公式,可以实现高效的 O(n*m)
最优解。
完成后,在最右边的列上进行简单扫描即可找到 "most profitable" 路径的目的地,您可以通过返回上面的内容并写下每个路径,从那里重建实际路径每一步的决定。
假设我们有这样的板子:
并且我们希望通过以下移动模式从左到右找到最有利可图的路径:
例如,在此版块中,最有利可图的路径是:
即{2, 0} -> {2, 1} -> {3, 2} -> {3, 3}
我写了下面的代码:
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());
}
}
但是找不到路径... 输出:
Board :
[-7, 1, 18]
[88, 56, 18]
[-18, -13, 100]
The most profitable path :
[{1, 0}, {2, 1}, {1, 1}, {2, 2}]
我的代码有什么问题?
我知道这不是找到最赚钱路径的最佳算法,而且速度很慢。
在 n*n
板中,路径数为:
n * 2 ^ (n-1) < number of paths < n * 3 ^ (n-1)
在这个算法中,我们正在检查所有路径以找到最有利可图的路径。
谢谢。
您正在求解 Longest Path Problem, which is usually NP-Complete. However, in your case, you actually have a Directed Acyclic Graph,因此可以使用以下递推公式获得有效的解决方案:
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
通过应用 Dynamic Programming 技术来实现此公式,可以实现高效的 O(n*m)
最优解。
完成后,在最右边的列上进行简单扫描即可找到 "most profitable" 路径的目的地,您可以通过返回上面的内容并写下每个路径,从那里重建实际路径每一步的决定。