• 45-骑士周游问题


    问题简述

    将马随机放在国际象棋的8×8棋盘的某个方格中,马按走棋规则进行移动。要求每个方格只进入一次,走遍棋盘上全部64个方格

    思路

    步骤 (遍历 + 回溯)

    1. 创建棋盘chessBoard,一个二维数组
    2. 将当前位置设置已访问标记(当前 step),然后根据当前位置,计算出马下一步可走哪些位置(最多 8 个),将这些位置放入一个 ArrayList 当中
    3. 遍历 ArrayList 中存放的所有位置,递归着走。走得通,step+1,继续递归;走不通,就回溯
    4. 判断马是否走遍了:使用 step 和 走遍一个棋盘该走的步数 进行比较。如果没达到,则说明没有完成任务

    对 ↑ 进行优化 (贪心)

    • 因为马下一步可走位置有时不止一个;所以,制定不同的策略 (即马选择下一步时的算法),会得到不同的结果,效率也会有影响
    • 使用 贪心 对原来的算法进行优化
      • 确定 {局部最优解} 是什么
        • [最优策略] 让马先走 { next.next 可选步骤最少}的 next ← 贪心的局部最优解!
        • next.next 可选的步骤越多, 意味着你回溯的次数越多; 选择越少, 回溯的次数越少
      • 实现思路
        • 根据 <当前nextList 中每个Point的nextList的元素数目> 对 当前nextList 进行升序排序

    代码实现

    public class TravelChessBoard {
    	private static int X; // 棋盘列数
    	private static int Y; // 棋盘行数
    	private static int[][] chessBoard;
    	// 标记整个棋盘各个位置的访问情况(一维)
    	private static boolean[] isVisited;
    	// 所有位置是否都已被访问
    	private static boolean finished;
    	
    	public static void main(String[] args) {
    		X = 8;
    		Y = 8;
    		chessBoard = new int[Y][X];
    		isVisited = new boolean[X * Y];
    		long start = System.currentTimeMillis();
    		travelChessBoard(0, 0, 1); // 假定初始位置 (0,0)
    		long end = System.currentTimeMillis();
    		System.out.println("共耗时 " + (end - start) + " ms");
    		// 输出棋盘
    		for(int[] rows : chessBoard) {
    			for(int step : rows)
    				System.out.printf("%3d  ", step);
    			System.out.println();
    		}
    	}
    	
    	/**
    	 * 骑士周游问题
    	 * @param row 马当前的行坐标[0...X-1]
    	 * @param col 马当前的纵坐标[0...Y-1]
    	 * @param step 当前这是第几步 (初始值1; 刚开始把马放到棋盘上就已经算第1步了)
    	 */
    	public static void travelChessBoard(int row, int col, int step) {
    		// 假定可以 (ง •_•)ง
    		chessBoard[row][col] = step;
    		isVisited[row * X + col] = true;
    
    		ArrayList<Point> nextList = getNextPositions(new Point(col, row));
    		sortByNextSize(nextList); // 用贪心优化
    		while(! nextList.isEmpty()) {
    			Point p = nextList.remove(0);
    			// 判断该点是否已经访问过
    			if(! isVisited[p.y * X + p.x])
    				travelChessBoard(p.y, p.x, step + 1);
    		} // 说明当前这个位置没有可走的下一步了
    		
    		if(step < X * Y && ! finished) {
    			// 实则不行 (*/ω\*)
    			chessBoard[row][col] = 0;
    			isVisited[row * X + col] = false;
    		} else {
    			// 成了 (●ˇ∀ˇ●)
    			finished = true;
    		}
    	}
    	
    	public static void sortByNextSize(ArrayList<Point> list) {
    		// 根据 {当前nextList中每个Point的nextList的元素数目} 对 当前nextList 进行升序排序
    		list.sort(new Comparator<Point>() {
    			@Override
    			public int compare(Point o1, Point o2) {
    				int size1 = getNextPositions(o1).size();
    				int size2 = getNextPositions(o2).size();
    				// ASC
    				if(size1 < size2)
    					return -1;
    				else if(size1 > size2)
    					return 1;
    				else 
    					return 0;
    			}
    		});
    	}
    	
    	/**
    	 * 根据 马 的当前位置, 计算出马的下一步可以走哪些位置
    	 * @param curPoint 封装马当前位置的Point对象
    	 * @return 下一步可走位置组成的集合
    	 */
    	public static ArrayList<Point> getNextPositions(Point curPoint) {
    		ArrayList<Point> nextList = new ArrayList<>();
    		Point p1 = new Point();
    		// 能不能走 (5) 的位置
    		if((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y - 1) >= 0)
    			nextList.add(new Point(p1));
    		// 能不能走 (6) 的位置
    		if((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y - 2) >= 0)
    			nextList.add(new Point(p1));
    		// 能不能走 (7) 的位置
    		if((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y - 2) >= 0)
    			nextList.add(new Point(p1));
    		// 能不能走 (0) 的位置
    		if((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y - 1) >= 0)
    			nextList.add(new Point(p1));
    		// 能不能走 (1) 的位置
    		if((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y + 1) < Y)
    			nextList.add(new Point(p1));
    		// 能不能走 (2) 的位置
    		if((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y + 2) < Y)
    			nextList.add(new Point(p1));
    		// 能不能走 (3) 的位置
    		if((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < Y)
    			nextList.add(new Point(p1));
    		// 能不能走 (4) 的位置
    		if((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < Y)
    			nextList.add(new Point(p1));
    		return nextList;
    	}
    }
    

    结果显示

    未使用贪心优化

    使用贪心优化后

  • 相关阅读:
    vite启用host代理,自动无限刷新问题
    机器人语音交互
    让or使用索引
    leetcode 77. Combinations 组合(中等)
    leetcode 257. Binary Tree Paths 二叉树的所有路径(简单)
    leetcode 934. Shortest Bridge 最短的桥(中等)
    为什么 SQL 语句使用了索引,但却还是慢查询?
    leetcode 47. Permutations II 全排列 II(中等)
    leetcode 79. Word Search 单词搜索
    leetcode 126. Word Ladder II 单词接龙 II(困难)
  • 原文地址:https://www.cnblogs.com/liujiaqi1101/p/12489986.html
Copyright © 2020-2023  润新知