需求來源:4399之馬踏棋盤小遊戲:http://www.4399.com/flash/146267_2.htm
遊戲規則:將國際象棋馬放入一個6x6的棋盤中,隨機指定一個初始位置,求棋子走完棋盤的步法
解題思路:二維數組模擬棋盤,記錄其步數,再使用一個boolean型的二維數組模擬棋盤,判斷其位置是否已經走過
使用Java的Point類表示棋子,根據國際象棋馬兒的走法可知一個棋子最多有8種走法編寫一個方法,返回值
爲當前棋子所有下一位置的集合,然後遞歸調用該方法,每次調用步數step+1,遞歸回溯爲判斷步數是否已經
到達棋盤的位置個數length,如果遞歸深度即步數step未到達length則回溯(將棋盤步數和已訪問位置重置)
優化:遞歸先走下一棋子步數最多的位置,這樣可以有效減少代碼回溯的次數(貪心算法)
算法思想:動態規劃算法之回溯法
優化思想:貪心算法減少回溯次數
代碼實現:(回溯法)
import java.awt.Point;
import java.util.LinkedList;
/**
* 馬踏棋盤算法
* 回溯法、貪心算法
* @author com
*
*/
public class ChessBoard {
private int X; // 棋盤的橫座標
private int Y; // 棋盤的縱座標
private int[][] checkerboard; // 自定義二維數組棋盤
private boolean[][] visited; // 判斷棋子是否訪問過
private boolean finished; // 判斷遞歸是否完成
private Point p; // 遞歸取出的棋子位置
// 構造函數初始化對象
public ChessBoard(int x,int y) {
X = x; Y = y;
checkerboard = new int[x][y];
visited = new boolean[x][y];
}
public static void main(String[] args) {
System.out.println("----------------- 遊戲開始 -----------------\n");
long start = System.currentTimeMillis();
ChessBoard chess = new ChessBoard(6,6); // 初始化棋盤
chess.traceback(1, 3, 1); // 遞歸+回溯,完成棋盤走法
// 打印棋盤結果
for(int[] x:chess.checkerboard) {
for(int y:x) {
System.out.printf("%d\t",y);
}System.out.println("\n");
}
long end = System.currentTimeMillis();
long time = (end - start)/1000;
System.out.println("一共耗時: "+ time + " 秒");
}
/**
* 核心算法,回溯實現
* @param checkerboard 棋盤
* @param visited 是否訪問標記
* @param row 棋子當前行座標
* @param col 棋子當前列座標
* @param step 棋子當前步數
*/
public void traceback(int x,int y,int step) {
checkerboard[x][y] = step; // 將當前的步數記錄在棋盤上
visited[x][y] = true; // 將當前位置標記爲已訪問過
LinkedList<Point> list = next(new Point(x,y)); // 當前點所有可能步數的集合
// 循環遍歷集合,直到爲空跳出循環
while(!list.isEmpty()) {
p = list.remove(0); // 取出下一個可以走的位置
// 判斷該點是否已經訪問過
if(!visited[p.x][p.y]) {
traceback(p.x, p.y, step+1); // 遞歸
}
}
// 回溯
if(step < X*Y && !finished) {
checkerboard[x][y] = 0; // 棋盤步數重置
visited[x][y] = false; // 訪問記錄重置
}else {
finished = true;
}
}
/**
* 將當前棋子的下一個位置的所有位置存入list中
* @param curPoint 當前棋子
* @return list 棋子下一個位置所有可能的集合
*/
public LinkedList<Point> next(Point curPoint){
LinkedList<Point> list = new LinkedList<Point>();
Point p = new Point();
// 棋子可以走位置1,x+2<X,y-1>=0
if((p.x = curPoint.x + 2) < X && (p.y = curPoint.y - 1) >= 0) {
list.add(new Point(p));
}
// 棋子可以走位置2,x+1<X,y-2>=0
if((p.x = curPoint.x + 1) < X && (p.y = curPoint.y - 2) >= 0) {
list.add(new Point(p));
}
// 棋子可以走位置3,x-1>=0,y-2>=0
if((p.x = curPoint.x - 1) >= 0 && (p.y = curPoint.y - 2) >= 0) {
list.add(new Point(p));
}
// 棋子可以走位置4,x-2>=0,y-1>=0
if((p.x = curPoint.x - 2) >= 0 && (p.y = curPoint.y - 1) >= 0) {
list.add(new Point(p));
}
// 棋子可以走位置5,x-2>=0,y+1<Y
if((p.x = curPoint.x - 2) >= 0 && (p.y = curPoint.y + 1) < Y) {
list.add(new Point(p));
}
// 棋子可以走位置6,x-1>0,y+2<Y
if((p.x = curPoint.x - 1) >= 0 && (p.y = curPoint.y + 2) < Y) {
list.add(new Point(p));
}
// 棋子可以走位置7,x+1<X,y+2<Y
if((p.x = curPoint.x + 1) < X && (p.y = curPoint.y + 2) < Y) {
list.add(new Point(p));
}
// 棋子可以走位置8,x+2<X,y+1<Y
if((p.x = curPoint.x + 2) < X && (p.y = curPoint.y + 1) < Y) {
list.add(new Point(p));
}
return list;
}
}
算法優化:(貪心算法)
import java.awt.Point;
import java.util.Comparator;
import java.util.LinkedList;
/**
* 馬踏棋盤算法
* 回溯法、貪心算法
* @author com
*
*/
public class ChessBoard {
private int X; // 棋盤的橫座標
private int Y; // 棋盤的縱座標
private int[][] checkerboard; // 自定義二維數組棋盤
private boolean[][] visited; // 判斷棋子是否訪問過
private boolean finished; // 判斷遞歸是否完成
private Point p; // 遞歸取出的棋子位置
// 構造函數初始化對象
public ChessBoard(int x,int y) {
X = x; Y = y;
checkerboard = new int[x][y];
visited = new boolean[x][y];
}
public static void main(String[] args) {
System.out.println("----------------- 遊戲開始 -----------------\n");
long start = System.currentTimeMillis();
ChessBoard chess = new ChessBoard(6,6); // 初始化棋盤
chess.traceback(1, 3, 1); // 遞歸+回溯,完成棋盤走法
// 打印棋盤結果
for(int[] x:chess.checkerboard) {
for(int y:x) {
System.out.printf("%d\t",y);
}System.out.println("\n");
}
long end = System.currentTimeMillis();
long time = (end - start)/1000;
System.out.println("一共耗時: "+ time + " 秒");
}
/**
* 核心算法,回溯實現
* @param checkerboard 棋盤
* @param visited 是否訪問標記
* @param row 棋子當前行座標
* @param col 棋子當前列座標
* @param step 棋子當前步數
*/
public void traceback(int x,int y,int step) {
checkerboard[x][y] = step; // 將當前的步數記錄在棋盤上
visited[x][y] = true; // 將當前位置標記爲已訪問過
LinkedList<Point> list = next(new Point(x,y)); // 當前點所有可能步數的集合
sort(list); // 貪心算法升序排序優化
// 循環遍歷集合,直到爲空跳出循環
while(!list.isEmpty()) {
p = list.remove(0); // 取出下一個可以走的位置
// 判斷該點是否已經訪問過
if(!visited[p.x][p.y]) {
traceback(p.x, p.y, step+1); // 遞歸
}
}
// 回溯
if(step < X*Y && !finished) {
checkerboard[x][y] = 0; // 棋盤步數重置
visited[x][y] = false; // 訪問記錄重置
}else {
finished = true;
}
}
/**
* 將當前棋子的下一個位置的所有位置存入list中
* @param curPoint 當前棋子
* @return list 棋子下一個位置所有可能的集合
*/
public LinkedList<Point> next(Point curPoint){
LinkedList<Point> list = new LinkedList<Point>();
Point p = new Point();
// 棋子可以走位置1,x+2<X,y-1>=0
if((p.x = curPoint.x + 2) < X && (p.y = curPoint.y - 1) >= 0) {
list.add(new Point(p));
}
// 棋子可以走位置2,x+1<X,y-2>=0
if((p.x = curPoint.x + 1) < X && (p.y = curPoint.y - 2) >= 0) {
list.add(new Point(p));
}
// 棋子可以走位置3,x-1>=0,y-2>=0
if((p.x = curPoint.x - 1) >= 0 && (p.y = curPoint.y - 2) >= 0) {
list.add(new Point(p));
}
// 棋子可以走位置4,x-2>=0,y-1>=0
if((p.x = curPoint.x - 2) >= 0 && (p.y = curPoint.y - 1) >= 0) {
list.add(new Point(p));
}
// 棋子可以走位置5,x-2>=0,y+1<Y
if((p.x = curPoint.x - 2) >= 0 && (p.y = curPoint.y + 1) < Y) {
list.add(new Point(p));
}
// 棋子可以走位置6,x-1>0,y+2<Y
if((p.x = curPoint.x - 1) >= 0 && (p.y = curPoint.y + 2) < Y) {
list.add(new Point(p));
}
// 棋子可以走位置7,x+1<X,y+2<Y
if((p.x = curPoint.x + 1) < X && (p.y = curPoint.y + 2) < Y) {
list.add(new Point(p));
}
// 棋子可以走位置8,x+2<X,y+1<Y
if((p.x = curPoint.x + 2) < X && (p.y = curPoint.y + 1) < Y) {
list.add(new Point(p));
}
return list;
}
/**
* 算法優化:正序排序
* 貪心算法:將當前棋子下下位置的集合按照個數進行排序
* 優化思想:先將可能多的下下位置的子先下了,再回溯下少位置的子
* @param list
*/
public void sort(LinkedList<Point> list) {
list.sort(new Comparator<Point>() {
@Override
public int compare(Point p1, Point p2) {
return next(p1).size() - next(p2).size();
}
});
}
}
運行結果:
棋子最多有8個位置可以走(位置對應代碼註釋的位置x)
遊戲鏈接:http://www.4399.com/flash/146267_2.htm
遊戲成功截圖: