问题描述
将马随机放在国际象棋的Board[0~7][0~7]的某个方格中,马按走棋规则进行移动。,走遍棋盘上全部64个方格。编制非递归程序,求出马的行走路线,并按求出的行走路线,将数字1,2,…,64依次填入一个8×8的方阵,输出之。
解题思路
我们用一个二维数组模拟马走的方向,通过函数move(x,y),来达到马走。 如果棋盘next_x>=0 && next_x<=4 && next_y>=0 && next_y<=3表示next_x,next_y在棋盘内,棋盘qipan[next_x][next_y] = = 0表示起盘没有走过,即下一步满足这些条件,则把下一步置为走过的状态,step++,调用move(next_x,next_y)函数,调用完再回溯。
贪心算法优化
贪心算法(又称贪婪算法),是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。它只考虑局部的最优,从而让总体也最优。就我们这个马踏棋盘来说,我们每走一步都取最优的那个选择(而不是随便选择下一步),从而让整体的算法也最优。
那么此问题的局部最优选择是什么呢?答案是,对于当前的一步,如果它的下一步有多种选择,我们应该选择它的下一步中的下一步选择最少的那个作为它的下一步。理由:因为只有在这样的选择方案下,当方案不符时,能够最快的回溯到当前的这一步上来(例如,它的下一步有4种选择,并且这4种选择的各自的下一步又分别有3,10,1,8种选择,而现在只有选择了下一步中的某一个才能成功,那么问题就是如何以最快的速度遍历出它的下一步中正确选择,也就是说当当前选择不对时,如何快速回溯到当前这一步,很显然,那只有它的下一步的下一步选择越少才会越快的结束这个选择),接着选择当前这一步的下一步选择次少作为它的下一步,依此类推。
代码
package TanXin;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Scanner;
public class MataQipanTanxi2 {
private static int X; // 棋盘的列数
private static int Y; // 棋盘的行数
private static boolean visited[]; // 用来标记棋盘上各个位置是否被访问过
private static boolean finished; // 用来标记是否期盼所有位置均被访问(意味着已成功)
private static int count = 1;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
do{
System.out.print("请输入棋盘的行数和列数:");
Y = sc.nextInt();
X = sc.nextInt();
}while(X <= 0 || Y <= 0);
int row, column;
do{
System.out.print("请输入起始位置(先行数后列数):");
row = sc.nextInt();
column = sc.nextInt();
}while(row <= 0 || row > Y || column <= 0 || column > X);
int[][] chessBoard = new int[Y][X];
visited = new boolean[X * Y];
long start = System.currentTimeMillis();
traversalChessBoard(chessBoard, row - 1, column - 1, 1);
long end = System.currentTimeMillis();
System.out.println("共耗时:" + (end - start) + "ms");
for(int[] rows : chessBoard){
for(int columns : rows){
System.out.print(columns + "\t");
}
System.out.println();
}
sc.close();
}
public static void traversalChessBoard(int[][] chessBoard, int row, int column, int step) {
chessBoard[row][column] = step;
visited[row * X + column] = true; // 此位置已访问
ArrayList<Point> ps = next(new Point(column, row)); // 由当前位置得到下一次所有位置的集合
sort(ps); // 按照当前(new Point(column, row))这步的下一步的下一步选择数目进行非递减排序
while (!ps.isEmpty()) {
Point p = ps.remove(0); // 每次选择仍然未选中的下一步的下一步选择数目最少的下一步作为当前这步的下一步
if (!visited[p.y * X + p.x]) { // 这个位置没有访问,那么就从这个位置开始进行下一次访问
traversalChessBoard(chessBoard, p.y, p.x, step + 1);
}
}
if(step < X * Y && !finished){ // (step < X * Y)这个条件成立,有两种情况:第一种,棋盘到目前为止仍没有走完;第二种,棋盘已经走完过,此时在回溯的过程中
chessBoard[row][column] = 0; // 如果整个棋盘最终全部为零,则表示无解
visited[row * X + column] = false;
}else{ // 此处代表已成功走出覆盖棋盘的完整路径,如果此时将结果输出(去掉finished变量),以便回溯可以得到所有的结果
finished = true;
}
}
// 在当前位置p处,下一次的位置(最多有8个位置)
public static ArrayList<Point> next(Point p) {
ArrayList<Point> ps = new ArrayList<Point>();
Point p1 = new Point(p);
if ((p1.x = p.x - 2) >= 0 && (p1.y = p.y - 1) >= 0) {
ps.add(new Point(p1));
}
if ((p1.x = p.x - 1) >= 0 && (p1.y = p.y - 2) >= 0) {
ps.add(new Point(p1));
}
if ((p1.x = p.x + 1) < X && (p1.y = p.y - 2) >= 0) {
ps.add(new Point(p1));
}
if ((p1.x = p.x + 2) < X && (p1.y = p.y - 1) >= 0) {
ps.add(new Point(p1));
}
if ((p1.x = p.x + 2) < X && (p1.y = p.y + 1) < Y) {
ps.add(new Point(p1));
}
if ((p1.x = p.x + 1) < X && (p1.y = p.y + 2) < Y) {
ps.add(new Point(p1));
}
if ((p1.x = p.x - 1) >= 0 && (p1.y = p.y + 2) < Y) {
ps.add(new Point(p1));
}
if ((p1.x = p.x - 2) >= 0 && (p1.y = p.y + 1) < Y) {
ps.add(new Point(p1));
}
return ps;
}
// 根据当前的这一步的所有下一步的选择数目进行非递减排序
public static void sort(ArrayList<Point> ps){
ps.sort(new Comparator<Point>() {
@Override
public int compare(Point o1, Point o2) {
int count1 = next(o1).size(); // 当前这一步o1的下一步的选择数目
int count2 = next(o2).size();
if (count1 < count2) {
return -1;
} else if (count1 == count2) {
return 0;
} else {
return 1;
}
}
});
}
}
运行结果
请输入棋盘的行数和列数:8 8
请输入起始位置(先行数后列数):3 1
共耗时:30ms
29 2 43 18 31 4 45 20
42 17 30 3 44 19 32 5
1 28 53 56 63 60 21 46
16 41 62 59 54 57 6 33
27 12 55 52 61 64 47 22
40 15 38 25 58 51 34 7
11 26 13 50 9 36 23 48
14 39 10 37 24 49 8 35