马踏棋盘(骑士周游列国)问题(Java)

整个问题的解决思路就是递归回溯+贪心算法

递归代码如下:

/**
     * 完成骑士周游问题的算法
     * @param chessboard 棋盘
     * @param row 骑士当前所在位置的行(从0开始)
     * @param column 骑士当前所在位置的列(从0开始)
     * @param step 第几步(从1开始)
     */
    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));

        //对ps进行非递减排序来减少递归次数
        sort(ps);

        //遍历ps
        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 ){
            chessboard[row][column] = 0;
            visited[row * X + column] = false;
        } else {
            finished = true;
        }

    }

学完这个感觉到贪心算法是真的牛逼,废话少说,直接上图!

没使用贪心算法时耗费46478毫秒:

使用了贪心算法后只用了39毫秒:

贪心算法的思路是对马下一步能走的位置的集合进行非递减排序,通过减少回溯的次数来达到缩短搜索时间的目的。代码如下:

/**
     * 对马下一步能走的位置的集合进行非递减排序,通过减少回溯的次数来达到缩短搜索时间的目的
     * @param 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();
                int count2 = next(o2).size();
                if ( count1 < count2 ) {
                    return -1;
                } else if (count1 == count2){
                    return 0;
                } else {
                    return 1;
                }
            }
        });
    }

完整代码如下:

package Algorithm.horse;

import java.awt.*;
import java.util.ArrayList;
import java.util.Comparator;

/**
 * 马踏棋盘
 */
public class HorseChessboard {

    private static int X;//列
    private static int Y;//行
    private static boolean[] visited;//该数组标记各个位置是否被访问过
    private static boolean finished;//true表示成功,false表示失败

    public static void main(String[] args) {

        X = Y = 8;
        int row = 1;
        int column = 1;

        int[][] chessboard = new int[X][Y];//创建棋盘
        visited = new boolean[X * Y];

        System.out.println("马踏棋盘开始:");
        long start = System.currentTimeMillis();
        traversalChessboard(chessboard, row - 1, column - 1, 1);
        long end = System.currentTimeMillis();
        System.out.println("共耗时:"+ (end - start) +"毫秒");

        //输出结果
        for ( int[] rows: chessboard ) {
            for ( int step: rows ) {
                System.out.print(step+"\t");
            }
            System.out.println();
        }

    }

    /**
     * 完成骑士周游问题的算法
     * @param chessboard 棋盘
     * @param row 骑士当前所在位置的行(从0开始)
     * @param column 骑士当前所在位置的列(从0开始)
     * @param step 第几步(从1开始)
     */
    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));

        //对ps进行非递减排序来减少递归次数
        sort(ps);

        //遍历ps
        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 ){
            chessboard[row][column] = 0;
            visited[row * X + column] = false;
        } else {
            finished = true;
        }

    }

    /**
     * 根据当前位置计算马还能走哪些位置,并将这些位置放入到一个集合中
     * @param curPoint
     * @return
     */
    public static ArrayList<Point> next(Point curPoint){
        ArrayList<Point> ps = new ArrayList<Point>();

        Point p1 = new Point();

        if ( (p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y - 1) >= 0 ) {
            ps.add(new Point(p1));
        }

        if ( (p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y - 2) >= 0 ) {
            ps.add(new Point(p1));
        }

        if ( (p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y - 2) >= 0 ) {
            ps.add(new Point(p1));
        }

        if ( (p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y - 1) >= 0 ) {
            ps.add(new Point(p1));
        }

        if ( (p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y + 1) < Y ) {
            ps.add(new Point(p1));
        }

        if ( (p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y + 2) < Y ) {
            ps.add(new Point(p1));
        }

        if ( (p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < Y ) {
            ps.add(new Point(p1));
        }

        if ( (p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < Y ) {
            ps.add(new Point(p1));
        }

        return ps;

    }

    /**
     * 对马下一步能走的位置的集合进行非递减排序,通过减少回溯的次数来达到缩短搜索时间的目的
     * @param 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();
                int count2 = next(o2).size();
                if ( count1 < count2 ) {
                    return -1;
                } else if (count1 == count2){
                    return 0;
                } else {
                    return 1;
                }
            }
        });
    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值