動態規劃之回溯法(馬踏棋盤)

需求來源: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

遊戲成功截圖:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章