zoukankan      html  css  js  c++  java
  • 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;
    	}
    }
    

    结果显示

    未使用贪心优化

    使用贪心优化后

  • 相关阅读:
    哈希表
    跳表
    哈夫曼之谜
    选择树、判定树和查找树

    将gbk字符串转换成utf-8,存储到注册表中后,再次从注册表读取转换成gbk,有问题!!!
    函数内部还是不要使用 strtok()
    没想到: System.out.println(n1 == f1 ? n1 : f1);
    在不同DPI屏幕环境下,让图标显示的尺寸保持不变,使用 LoadImage() 加载图标
    在多线程中显示模态窗口,出现异常现象
  • 原文地址:https://www.cnblogs.com/liujiaqi1101/p/12489986.html
Copyright © 2011-2022 走看看