日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java 马踏棋盘优化_我所知道的十大常用算法之马踏棋盘算法(深度搜索、贪心思想优化 )...

發布時間:2023/12/2 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 马踏棋盘优化_我所知道的十大常用算法之马踏棋盘算法(深度搜索、贪心思想优化 )... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言需求

今天我們學習的是馬踏棋盤算法,我們還是從一個場景里引入看看

馬踏棋盤算法也被稱為騎士周游問題

將馬隨機放在國際象棋的6×6棋盤Board0~5的某個方格中

提示:馬按走棋規則(馬走日字)進行移動

要求:每個方格只進入一次,走遍棋盤上全部64個方格

一、馬踏棋盤問題

馬踏棋盤問題(騎士周游問題)實際上是:圖的深度優先搜索(DFS)的應用

還記得圖的深度優先搜索(DFS)嗎?

有些模糊或者不記得小伙伴可以看往期文章:圖(廣度優先與深度優先)

那么按照我們的簡單思路,是不是要一個位置一個位置去踩坑看看?

那么按照我們的深度優先搜索,就要一步步走下去,直至達成任務

當我們的所選第三步的位置,無法達成完成任務

那么我們需要回溯,將原第三步更換到下一個位置里去

在以新第三步開始,進行搜索,也要一步步走下去,直至達成任務

二、通過示例來認識算法

根據我們之前簡單的思路,首先我們需要創建一個棋盤的數組

當我們做出選擇下一步的時候,我們需要將當前的位置標記為已訪問,并根據當前位置計算出馬兒能走那些位置,并放入到一個集合中里去

當然我們可以根據棋盤的情況來判斷是否可以進行計算

注意::馬兒不同的走法、會得到不同的結果,效率也會有影響(需優化)

規則判斷是否可走

那么我怎么知道這些位置是否可走呢?我是怎么計算出來的呢?

首先我們先分析當前位置的x、y坐標,按照規則進行計算:(馬走日字)

我們先分析一下象棋里的馬走日是怎么樣的吧

馬走日所說的是馬從提棋位置到落棋位置是一個“日”子的對角線,在沒有棋子踩住馬腳時,馬是可以隨意走哪個方向的日字都是可以的

在有其他棋子在馬的如圖相關位置時,馬就不能走該方向的日字了,我們也熟稱“踩馬腳了”。注意無論踩馬腳的棋子是己方的棋子還是敵方的棋子,被踩方向的日字都不能走了

如果四只馬腳都被踩了,那么這只馬哪里都走不了了(如圖)

在我們這個問題中,還請你看圖關聯看懂馬兒怎么走的,即稱馬走日

當我們知道規則怎么玩了,就可以從圖上看出來,每個點與當前點的關系

那么我們的馬兒剩下的點與當前是什么關系呢?怎么走呢?

騎士周游算法思路

我們創建一個類存放棋盤行、列,并記錄棋盤上的是否被訪問過public class HorseChessboard {

private static int x;//棋盤的列數

private static int y;//棋盤的行數

//創建一個數組,標記棋盤的各個位置是否被訪問過

private static boolean visited[];

//使用一個屬性,標記是否棋盤的所有位置都被訪問

private static boolean finished; // 如果為true,表示成功

}

我們使用Point 類來表示 (x, y) 坐標空間中的位置的點public class Point extends Point2D implements java.io.Serializable {

public int x;

public int y;

private static final long serialVersionUID = -5276940640259749850L;

public Point() {

this(0, 0);

}

public Point(Point p) {

this(p.x, p.y);

}

public Point(int x, int y) {

this.x = x;

this.y = y;

}

//以雙精度型返回點的 X 坐標。

public double getX() {

return x;

}

//以雙精度型返回點的 Y 坐標。

public double getY() {

return y;

}

//返回此點的位置。

@Transient

public Point getLocation() {

return new Point(x, y);

}

//將點的位置設為指定位置

public void setLocation(Point p) {

setLocation(p.x, p.y);

}

//將此點更改為具有指定位置

public void setLocation(int x, int y) {

move(x, y);

}

//將此點的位置設為指定的雙精度坐標

public void setLocation(double x, double y) {

this.x = (int) Math.floor(x+0.5);

this.y = (int) Math.floor(y+0.5);

}

//將此點移動到 (x,y) 坐標平面中的指定位置。

public void move(int x, int y) {

this.x = x;

this.y = y;

}

//平移 (x,y) 位置的點,沿 x 軸平移 dx,沿 y 軸平移 dy,移動后得到點 (x+dx, y+dy)

public void translate(int dx, int dy) {

this.x += dx;

this.y += dy;

}

//確定兩個點是否相等。

public boolean equals(Object obj) {

if (obj instanceof Point) {

Point pt = (Point)obj;

return (x == pt.x) && (y == pt.y);

}

return super.equals(obj);

}

// 返回此點的字符串表示形式及其在 (x,y) 坐標空間中的位置

public String toString() {

return getClass().getName() + "[x=" + x + ",y=" + y + "]";

}

}

根據思路,需要根據當前位置判斷馬兒能走那些位置,并將結果放入ArrayList集合中

public class HorseChessboard {

//省略其他關鍵性代碼....

/**

* 功能:根據當前位置(Point對象),計算馬兒還能走哪些位置(Point),并放入到一個集合中(ArrayList),最多有8個位置

* @param curPoint

* @return

*/

public static ArrayList next(Point curPoint){

ArrayList ps = new ArrayList<>();

//創建一個點

Point p1 = new Point();

//判斷馬兒是否能走5的位置

if((p1.x = curPoint.x - 2) >=0 && (p1.y = curPoint.y+1) >=0 ){

ps.add(new Point(p1));

}

return ps;

}

}

而其他點的位置與當前位置關系,我們之前也使用圖解的方式分析,現在代碼實現

public class HorseChessboard {

//省略其他關鍵性代碼....

/**

* 功能:根據當前位置(Point對象),計算馬兒還能走哪些位置(Point),并放入到一個集合中(ArrayList),最多有8個位置

* @param curPoint

* @return

*/

public static ArrayList next(Point curPoint){

ArrayList ps = new ArrayList<>();

//創建一個點

Point p1 = new Point();

//表示馬兒可以走5這個位置

if((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y -1) >= 0) {

ps.add(new Point(p1));

}

//判斷馬兒可以走6這個位置

if((p1.x = curPoint.x - 1) >=0 && (p1.y=curPoint.y-2)>=0) {

ps.add(new Point(p1));

}

//判斷馬兒可以走7這個位置

if ((p1.x = curPoint.x + 1) < x && (p1.y = curPoint.y - 2) >= 0) {

ps.add(new Point(p1));

}

//判斷馬兒可以走0這個位置

if ((p1.x = curPoint.x + 2) < x && (p1.y = curPoint.y - 1) >= 0) {

ps.add(new Point(p1));

}

//判斷馬兒可以走1這個位置

if ((p1.x = curPoint.x + 2) < x && (p1.y = curPoint.y + 1) < y) {

ps.add(new Point(p1));

}

//判斷馬兒可以走2這個位置

if ((p1.x = curPoint.x + 1) < x && (p1.y = curPoint.y + 2) < y) {

ps.add(new Point(p1));

}

//判斷馬兒可以走3這個位置

if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < y) {

ps.add(new Point(p1));

}

//判斷馬兒可以走4這個位置

if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < y) {

ps.add(new Point(p1));

}

return ps;

}

}

那么會不會有小伙伴有疑惑??

為什么走五那個位置就要>=0呢,走七的位置就要

我們先分析走五的位置的時候,為什么要>=0

同理,小于x,小于y代表我們只選擇在棋盤內的點,超出的則不能走

騎士周游算法實踐

往期我們使用的是二維數組代表這個點是否被訪問過

但這里是36步都需要走一遍,那么我們其實可以使用一維數組進行表示

這樣我們可以是用公式:馬兒所在行 * 棋盤行 +馬兒所在列 = 馬兒下標 + 1public class HorseChessboard {

//省略其他關鍵性代碼....

/**

* 完成騎士周游問題的算法

* @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;

//row = 3 X = 6 column = 3 = 3 * 6 + 3 = 21 -1 = 20

visited[row * x + column] = true; //標記該位置已經訪問

//獲取當前位置可以走的下一個位置的集合

ArrayList ps = next(new Point(column, row));

}

}

當我們獲取到當前位置可以走的下一個位置的集合,就進行遍歷遞歸public class HorseChessboard {

//省略其他關鍵性代碼....

/**

* 完成騎士周游問題的算法

* @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;

//row = 3 X = 6 column = 3 = 3 * 6 + 3 = 21 -1 = 20

visited[row * x + column] = true; //標記該位置已經訪問

//獲取當前位置可以走的下一個位置的集合

ArrayList ps = next(new Point(column, row));

//遍歷 ps

while(!ps.isEmpty()) {

Point p = ps.remove(0);//取出下一個可以走的位置

//判斷該點是否已經訪問過

if(!visited[p.y * X + p.x]) {//說明還沒有訪問過

traversalChessboard(chessboard, p.y, p.x, step + 1);

}

}

}

}

我們怎么區分當前節點的可以走的下一個位置的集合,是否一路就成功了呢?

使用step 和 應該走的步數比較:step = X * Y

假如當前節點的可以走的下一個位置的集合,沒有一路就成功,怎么辦?

取消該位置已訪問,并將棋盤置為0,說明該節點處于回溯狀態public class HorseChessboard {

//省略其他關鍵性代碼....

/**

* 完成騎士周游問題的算法

* @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;

//row = 4 X = 8 column = 4 = 4 * 8 + 4 = 36

visited[row * x + column] = true; //標記該位置已經訪問

//獲取當前位置可以走的下一個位置的集合

ArrayList ps = next(new Point(column, row));

//遍歷 ps

while(!ps.isEmpty()) {

Point p = ps.remove(0);//取出下一個可以走的位置

//判斷該點是否已經訪問過

if(!visited[p.y * x + p.x]) {//說明還沒有訪問過

traversalChessboard(chessboard, p.y, p.x, step + 1);

}

}

//判斷馬兒是否完成了任務,使用 step 和應該走的步數比較 ,

//如果沒有達到數量,則表示沒有完成任務,將整個棋盤置0

//說明: step < X * Y 成立的情況有兩種

//1. 棋盤到目前位置,仍然沒有走完

//2. 棋盤處于一個回溯過程

if(step < x * y && !finished ) {

chessboard[row][column] = 0;

visited[row * x + column] = false;

} else {

finished = true;

}

}

}

接下來,讓我們使用demo 測試一把這些思路與代碼

我們采用上圖的馬兒作為起始位置,來測試看看public class HorseChessboard {

//省略其他關鍵性代碼....

public static void main(String[] args) {

System.out.println("騎士周游算法,開始運行~~");

//測試騎士周游算法是否正確

x = 6;

y = 6;

int row = 4; //馬兒初始位置的行,從1開始編號

int column = 3; //馬兒初始位置的列,從1開始編號

//創建棋盤

int[][] chessboard = new int[x][y];

visited = new boolean[x * y];//初始值都是false

//測試一下耗時

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();

}

}

}

運行結果如下:

騎士周游算法,開始運行~~

共耗時: 40 毫秒

08 03 10 29 32 05

17 28 07 04 11 30

02 09 18 31 06 33

27 16 01 20 23 12

36 19 14 25 34 21

15 26 35 22 13 24

三、使用貪心思想進行優化

利用貪心算法的思想,對下一步的所有集合的數目, 進行非遞減排序

什么是非遞減?

遞增的情況是:1、2、3、4、5、6、7、8、9

遞減的情況是:9、8、7、6、5、4、3、2、1

非遞增的情況是:9、8、7、6、5、5、4、3、2、1

非遞減的情況是:1、2、2、3、3、4、4、5、6、7

目的:使馬兒走的下一步是下一步集合中可選性最少的,減少回溯可能性public class HorseChessboard {

//省略其他關鍵性代碼....

//根據當前這個一步的所有的下一步的選擇位置,進行非遞減排序, 減少回溯的次數

public static void sort(ArrayList ps) {

ps.sort(new Comparator() {

@Override

public int compare(Point o1, Point o2) {

// TODO Auto-generated method stub

//獲取到o1的下一步的所有位置個數

int count1 = next(o1).size();

//獲取到o2的下一步的所有位置個數

int count2 = next(o2).size();

if(count1 < count2) {

return -1;

} else if (count1 == count2) {

return 0;

} else {

return 1;

}

}

});

}

}

那么怎么使用呢,我們在算法里進行排序優化public class HorseChessboard {

//省略其他關鍵性代碼....

/**

* 完成騎士周游問題的算法

* @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;

//row = 3 X = 6 column = 3 = 3 * 6 + 3 = 21 -1 = 20

visited[row * x + column] = true; //標記該位置已經訪問

//獲取當前位置可以走的下一個位置的集合

ArrayList ps = next(new Point(column, row));

//對ps進行排序,排序的規則就是對ps的所有的Point對象的下一步的位置的數目,進行非遞減排序

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);

}

}

}

}public class HorseChessboard {

//省略其他關鍵性代碼....

public static void main(String[] args) {

System.out.println("騎士周游算法,開始運行~~");

//測試騎士周游算法是否正確

x = 6;

y = 6;

int row = 4; //馬兒初始位置的行,從1開始編號

int column = 3; //馬兒初始位置的列,從1開始編號

//創建棋盤

int[][] chessboard = new int[x][y];

visited = new boolean[x * y];//初始值都是false

//測試一下耗時

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();

}

}

}

運行結果如下:

騎士周游算法,開始運行~~

共耗時: 9 毫秒

08 03 10 29 32 05

17 28 07 04 11 30

02 09 18 31 06 33

27 16 01 20 23 12

36 19 14 25 34 21

15 26 35 22 13 24

從40毫秒 到9毫秒 這個速度還是很客觀的,相比之前的算法更優一些

總結

以上是生活随笔為你收集整理的java 马踏棋盘优化_我所知道的十大常用算法之马踏棋盘算法(深度搜索、贪心思想优化 )...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。