动态规划之回溯法(马踏棋盘)

2021-04-27 11:21:15 浏览数 (1)

需求来源:4399之马踏棋盘小游戏:http://www.4399.com/flash/146267_2.htm

游戏规则:将国际象棋马放入一个6x6的棋盘中,随机指定一个初始位置,求棋子走完棋盘的步法

解题思路:二维数组模拟棋盘,记录其步数,再使用一个boolean型的二维数组模拟棋盘,判断其位置是否已经走过

                使用Java的Point类表示棋子,根据国际象棋马儿的走法可知一个棋子最多有8种走法编写一个方法,返回值

                为当前棋子所有下一位置的集合,然后递归调用该方法,每次调用步数step 1,递归回溯为判断步数是否已经

                到达棋盘的位置个数length,如果递归深度即步数step未到达length则回溯(将棋盘步数和已访问位置重置)

                优化:递归先走下一棋子步数最多的位置,这样可以有效减少代码回溯的次数(贪心算法)

算法思想:动态规划算法之回溯法

优化思想:贪心算法减少回溯次数

代码实现:(回溯法)

代码语言:javascript复制
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("%dt",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 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 next(Point curPoint){

		LinkedList list = new LinkedList();
		Point p = new Point();
		// 棋子可以走位置1,x 2=0
		if((p.x = curPoint.x   2) < X && (p.y = curPoint.y - 1) >= 0) {
			list.add(new Point(p));
		}
		// 棋子可以走位置2,x 1=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= 0 && (p.y = curPoint.y   1) < Y) {
			list.add(new Point(p));
		}
		// 棋子可以走位置6,x-1>0,y 2= 0 && (p.y = curPoint.y   2) < Y) {
			list.add(new Point(p));
		}
		// 棋子可以走位置7,x 1

算法优化:(贪心算法)

代码语言:javascript复制
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("%dt",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 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 next(Point curPoint){

		LinkedList list = new LinkedList();
		Point p = new Point();
		// 棋子可以走位置1,x 2=0
		if((p.x = curPoint.x   2) < X && (p.y = curPoint.y - 1) >= 0) {
			list.add(new Point(p));
		}
		// 棋子可以走位置2,x 1=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= 0 && (p.y = curPoint.y   1) < Y) {
			list.add(new Point(p));
		}
		// 棋子可以走位置6,x-1>0,y 2= 0 && (p.y = curPoint.y   2) < Y) {
			list.add(new Point(p));
		}
		// 棋子可以走位置7,x 1 list) {
		list.sort(new Comparator() {
			@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

游戏成功截图:

0 人点赞