JAVA中的回溯算法解空間樹,八皇后問題以及騎士游歷問題超詳解

1.回溯算法的概念

回溯算法顧名思義就是有回溯的算法

回溯算法實際上一個類似枚舉的搜索嘗試過程,主要是在搜索嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就“回溯”返回,嘗試別的路徑。回溯法是一種選優搜索法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇并不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法,而滿足回溯條件的某個狀態的點稱為“回溯點”。許多復雜的,規模較大的問題都可以使用回溯法,有“通用解題方法”的美稱。

回溯算法的核心思想是:

  1. 問題分解:將原問題分解成若干個規模較小的子問題。
  2. 候選解的生成:從根節點開始,逐層生成候選解。
  3. 活節點:當前正在擴展的節點稱為活節點。
  4. 死節點:當前擴展的節點如果已經確定不能得到問題的解,則該節點稱為死節點。
  5. 剪枝:根據問題的限制條件,對已經確定的死節點進行剪枝。

2.回溯算法解空間樹的問題

?典型案例:

集合求冪集的解空間樹

意思就是一個集合s所有的子集的集合

問題:求集合S={1,2,3}的所有子集,依次選擇每個元素的過程就是一棵樹,如圖所示

在樹中,由從根節點到葉子結點的每條路徑上的權值組成三元組(0,0,0)~(1,1,1)都是解,如(0,0,1)表示子集{B,C},共2^{3}=8個解;因此,該樹被稱為解空間樹,高度為4。

為什么幫助理解,我們先看普通的遞歸方法是什么實現的

下面是迭代方法代碼實現:

import java.util.ArrayList;
import java.util.List;public class PowerSet {public static void main(String[] args) {List<String> originalSet =new ArrayList<>();originalSet.add("A");originalSet.add("B");originalSet.add("C");//這里又嵌套了一個list是為了使每一個元素都變成list//適用于多種算法和數據處理任務List<List<String>> powerSet =getPowerSet(originalSet);for (List<String> subset:powerSet){System.out.println(subset);}}private static List<List<String>> getPowerSet(List<String> originalSet) {List<List<String>> powerSet =new ArrayList<>();if (originalSet==null||originalSet.isEmpty()){return powerSet;}//添加空集powerSet.add(new ArrayList<>());for (String element:originalSet){int size =powerSet.size();for (int i =0;i<size;i++){List<String> subset =new ArrayList<>(powerSet.get(i));subset.add(element);powerSet.add(subset);}}return powerSet;}

現在讓我們來分析一下這個過程:

假設第一次迭代中,我們處理元素“A”:

  • 初始時,powerSet只包含一個空集[ ]。
  • 我們復制這個空集,然后添加“A”,得到["A"]。
  • 將["A"]添加到powerSet,現在powerSet是[ ],["A"]。

在二次迭代中處理“B”:

  • 現在powerSet有兩個子集:[ ]和["A"]。
  • 我們首先復制空集,然后添加“B”,得到["B"]。
  • 然后復制[“A”],然后添加“B”,得到["A","B"]。
  • 然后將這兩個新的子集添加到powerSet,現在powerSet有[ ],["A"],["B"],["A","B"]

然后三次迭代重復這個過程就可以了。

下面是回溯方法的代碼:

public class PowerSetWithBacktracking {public static void main(String[] args) {List<String> originalSet = new ArrayList<>();originalSet.add("A");originalSet.add("B");originalSet.add("C");List<List<String>> powerSet = new ArrayList<>();backtrack(0, originalSet, new ArrayList<>(), powerSet);for (List<String> subset : powerSet) {System.out.println(subset);}}private static void backtrack(int start, List<String> originalSet, List<String> currentSubset, List<List<String>> powerSet) {//將當前子集的副本添加到冪集powerSet中,這里使用new ArrayList<>()來復制當前的子集。powerSet.add(new ArrayList<>(currentSubset));//遍歷集合中的每個元素,從start開始for (int i =start;i<originalSet.size();i++){//做出選擇:包含當前元素currentSubset.add(originalSet.get(i));//遞歸調用:移動到下一個元素backtrack(i+1,originalSet,currentSubset,powerSet);//撤銷選擇:回溯currentSubset.remove(currentSubset.size()-1);}
}
}

分析過程:

步驟startcurrentSubsetpowerSet
第一次0[ ][[ ]]
第二次0["a"][[ ]]
第三次1["a","b"][[ ]]
第四次2["a","b","c"][[ ]]
第五次3["a","b","c"][[" "],["a","b","c"],["a","b"],["a"]]
第六次2["a","b"][[" "],["a","b","c"],["a","b"],["a"]]
第七次1["a"][[" "],["a","b","c"],["a","b"],["a"]]
第八次2["a","c"][[" "],["a","b","c"],["a","b"],["a"]]
第九次2["a","c"][[" "],["a","b","c"],["a","b"],["a"],["a","c"]]
第十次1["a"][[" "],["a","b","c"],["a","b"],["a"],["a","c"]]
第十一次1[ ][[" "],["a","b","c"],["a","b"],["a"],["a","c"]]
第十二次1["b"][[" "],["a","b","c"],["a","b"],["a"],["a","c"],["b"]]

?接下來依次推導就行了。整體的思路就是不斷遞歸下一個元素,然后從currentSubset中移除最后添加的元素,進行回溯,準備處理下一個元素。

3.八皇后問題

八皇后問題是一個以國際象棋為背景的問題:如何能夠在8×8的國際象棋棋盤上放置八個皇后,使得任何一個皇后都無法直接吃掉其他的皇后?為了達到此目的,任兩個皇后都不能處于同一條橫行、縱行或斜線上嗎,問有多少種擺法。八皇后問題可以推廣為更一般的n皇后擺放問題:這時棋盤的大小變為n×n,而皇后個數也變成n。當且僅當n?= 1或n?≥ 4時問題有解

public class EightQueens {private static final int n =8;private static int[] queens;//存儲皇后的位置private static int count =0;//解的計數public static void main(String[] args){queens=new int[n];for (int i =0;i<n;i++){queens[i]=-1;//初始化都為-1,表示都沒有放皇后}placeQueens(0);//從第0行開始放置皇后System.out.println("");}private static void placeQueens(int row) {if (row==n){count++;printBoard();return;}for (int col =0;col<n;col++){if (isSafe(row,col)){queens[row]=col;//在當前位置放置皇后placeQueens((row+1));//進入下一行queens[row]=-1;//回溯,撤銷當前行的皇后放置的位置}}}private static boolean isSafe(int row, int col) {//檢查列是否有沖突for (int i =0;i<row;i++ ){if (queens[i]==col){return false;}}//檢查左上對角線是否有沖突for (int i =row-1,j=col+1;i>=0&&j>=0;i--,j--){if (queens[i]==j){return false;}}//檢查右下對角線是否有沖突for (int i =row-1,j=col+1;i>=0&&j<n;i--,j++){if (queens[i]==j){return false;}}return true;}private static void printBoard() {System.out.println("第"+count+"種解法");for(int i =0;i<n;i++){for (int j=0;j<n;j++){if (queens[i]==j){System.out.println("Q");}else{System.out.println(". ");}}System.out.println();}System.out.println();}
}

過程分析:

回溯發生在兩種情況下:

  1. 當前行是最后一行(row==n-1),并且已經嘗試了所有可能的列,并且已經找到了一個解決方案,遞歸將終止,開始回溯
  2. 在遞歸過程中,當前行不是最后一行,但在下一行無法找到任何安全位置放置皇后。

?4.騎士游歷問題

騎士游歷問題是指,在國際象棋的棋盤(8行*8列)上,一個馬要遍歷棋盤,即走到棋盤上的每一格,并且每隔只到達一次。 設碼在棋盤的某一位置(x,y)上,按照“走馬日”的規則,下一步有8個方向走,如圖所示。 若給定起始位置(x0,y0),使用站和隊列探索出一條馬遍歷棋盤的路徑。

?算法步驟:

  • 定義棋盤大小和騎士的可能移動的方式。
  • 創建一個二維數組來表示棋盤,初始值設為-1表示未訪問。
  • ?編寫一個輔助函數來檢查當前位置是否合法。
  • 編寫一個遞歸函數來嘗試所有可能的移動,使用回溯法來尋找解。
  • 在主函數中調用遞歸函數,并從指定的起始點開始。


public class KnightTour{private static final int N =8;private static final int[][] moves={{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1}};public static void main(String[] args) {int[][] board =new int[N][N];for (int i =0;i<N;i++){for (int j =0;j<N;j++){board[i][j]=-1;}}//起始點int x =0,y =0;board[x][y]=0;if (!solveKTUtil(board,x,y,1)){System.out.println("此解決方案不存在");}else{printSolution(board);}}private static boolean solveKTUtil(int[][] board, int x, int y, int moveCount) {if (moveCount==N*N){return true;}for (int i =0;i<8;i++){int nextX =x+moves[i][0];int nextY =y+moves[i][1];if (isSafe(board,nextX,nextY)){board[nextX][nextY]=moveCount;if (solveKTUtil(board,nextX,nextY,moveCount+1)){return true;}else{board[nextX][nextY]=-1;//回溯}}}return false;}private static boolean isSafe(int[][] board, int X, int Y) {return X>=0&&Y>=0&&X<N&&Y<N&&board[X][Y]==-1;}private static void printSolution(int[][] board){for (int i =0;i<N;i++){for(int j =0;j<N;j++){System.out.print(board[i][j]+" ");}System.out.println();}}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/45075.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/45075.shtml
英文地址,請注明出處:http://en.pswp.cn/web/45075.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

E12.【C語言】練習:求兩個數的最大公約數

1.枚舉 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int main() {int a 0;int b 0;int tmp 0;scanf("%d %d", &a, &b);if (a < b){for (int i1; i < a; i){if (0a% i && 0b%i)tmp i;}}if (a>b){for (int i 1; i <…

[線性RNN系列] Mamba: S4史詩級升級

前言 iclr24終于可以在openreview上看預印本了 這篇&#xff08;可能是顛覆之作&#xff09;文風一眼c re組出品&#xff1b;效果實在太驚艷了&#xff0c;實驗相當完善&#xff0c;忍不住寫一篇解讀分享分享。 TL;DR &#xff08;overview&#xff09; Structured State-Sp…

Nginx 日志統計分析命令

統計訪問量最多的IP地址&#xff1a; awk {print $1} /path/to/nginx/access.log | sort | uniq -c | sort -nr | head -n 10統計不同狀態碼的出現次數&#xff1a; awk {print $9} /path/to/nginx/access.log | sort | uniq -c | sort -nr統計訪問量最多的URL&#xff1a; awk…

SQL Server端口配置指南

SQL Server是微軟推出的關系型數據庫管理系統&#xff0c;它支持多種操作系統平臺。默認情況下&#xff0c;SQL Server使用TCP/IP協議的1433端口進行通信。然而&#xff0c;出于安全或其他考慮&#xff0c;我們可能需要更改SQL Server實例的默認端口。本文將指導你如何更改SQL …

利率債與信用債的區別及其與債券型基金的關系

利率債與信用債的定義及其區別 定義 利率債&#xff1a; 定義&#xff1a;利率債是指由主權或類主權主體&#xff08;如中華人民共和國財政部、國家開發銀行等&#xff09;發行的債券。這些債券通常被認為沒有信用風險&#xff0c;因為它們由國家信用背書。特點&#xff1a;由…

【Python】 深入了解 Python 字典的 | 更新操作

我白天是個 搞笑廢物 表演不在乎 夜晚變成 憂傷怪物 撕扯著孤獨 我曾經是個 感性動物 小心地感觸 現在變成 無關人物 &#x1f3b5; 張碧晨/王赫野《何物》 Python 3.9 引入了一種新的字典更新操作&#xff0c;即使用 | 運算符合并字典。這種方式不僅簡潔…

xshell公鑰免密登錄

設備&#xff1a;一臺linux系統機器&#xff0c;一臺windows系統機器 軟件&#xff1a;xshell 要求&#xff1a;公鑰免密登錄 一、生成公鑰、私鑰 1、打開shell &#xff1b; 點擊工具 &#xff1b; 新建用戶生成密鑰向導 2、生成密鑰參數 密鑰類型&#xff1a;RS…

element ui ts table重置排序

#日常# 今天帶的實習生&#xff0c;在遇到開發過程中&#xff0c;遇到了element ui table 每次查詢的時候都需要重置排序方式&#xff0c;而且多個排序是由前端排序。 <el-table :data"tableData" ref"restTable"> </<el-table> <script…

bi項目筆記

1.bi是什么 bi項目就是商業智能系統&#xff0c;也就是數據可視畫、報表可視化系統&#xff0c;如下圖的就是bi項目了 2.技術棧

Linux rsync文件同步工具

scp的不足 1. 性能問題 單線程傳輸 SCP只使用單線程進行傳輸&#xff0c;這意味著在傳輸大文件或大量小文件時&#xff0c;其傳輸速度和效率可能不如其他多線程工具。 無法壓縮數據傳輸 SCP不支持內置的壓縮機制&#xff0c;這在傳輸大文件時會導致帶寬使用效率較低。 2.…

我花了5年時間訓練自己這種能力,希望你也能成功

人生最重要的能力是日拱一卒&#xff0c;即每天做一點點對自己有利的事并持續足夠長的時間。作者之前急于求成&#xff0c;減肥失敗。同事通過每月改進一件小事成功減肥且知識儲備豐富。作者受啟發后&#xff0c;通過走樓梯、換代糖等小改變&#xff0c;用 4 年減了 40 斤&…

Hive的基本操作(創建與修改)

必備知識 數據類型 基本類型 類型寫法字符char, varchar, string?整數tinyint, smallint, int?, bigint?小數float, double, numeric(m,n), decimal(m,n)?布爾值boolean?時間date?, timestamp? 復雜類型(集合類型) 1、數組&#xff1a;array<T> 面向用戶提供…

從頭開始搭建一套Elasticsearch集群

前言 剛開始使用ES接觸的就是rpm或者是云上提供的ES服務&#xff0c;基本上開箱即用。特別是云上的ES服務&#xff0c;開局就是集群版本&#xff0c;提供的是優化后的參數配置、開箱即匹配訪問鑒權及常用插件&#xff0c;如無特殊需要基本上屏蔽了所有細節&#xff0c;直接可投…

深入了解 MySQL 的 EXPLAIN 命令

一、什么是 EXPLAIN 命令&#xff1f; EXPLAIN 命令用于顯示 MySQL 如何執行某個 SQL 語句&#xff0c;尤其是 SELECT 語句。通過 EXPLAIN 命令&#xff0c;可以看到查詢在實際執行前的執行計劃&#xff0c;這對于優化查詢性能至關重要。 二、EXPLAIN 的基本用法 要使用 EXP…

如何禁用鍵盤上的特定鍵或快捷方式?這里有詳細步驟

要禁用特定的鍵盤鍵或快捷鍵嗎&#xff1f;微軟官方應用程序Microsoft PowerToys使這項任務變得非常簡單。以下是使用Microsoft PowerToys中的鍵盤管理器禁用特定鍵或快捷方式的快速指南。 如果你還沒有安裝Microsoft PowerToys 如果你的設備上沒有安裝Microsoft PowerToys&a…

springboot上傳圖片

前端的name的值必須要和后端的MultipartFile 形參名一致 存儲本地

3.2、matlab單目相機標定原理、流程及實驗

1、單目相機標定流程及步驟 單目相機標定是通過確定相機的內部和外部參數,以便準確地在圖像空間和物體空間之間建立映射關系。下面是單目相機標定的流程及步驟: 搜集標定圖像:使用不同角度、距離和姿態拍攝一組標定圖像,并確保標定板(可以是棋盤格或者圓形標定板)完整可…

鴻蒙開發:Universal Keystore Kit(密鑰管理服務)【匿名密鑰證明(C/C++)】

匿名密鑰證明(C/C) 在使用本功能時&#xff0c;需確保網絡通暢。 在CMake腳本中鏈接相關動態庫 target_link_libraries(entry PUBLIC libhuks_ndk.z.so)開發步驟 確定密鑰別名keyAlias&#xff0c;密鑰別名最大長度為64字節&#xff1b;初始化參數集&#xff1a;通過[OH_Huk…

AcWing 667. 游戲時間

讀取兩個整數 A&#x1d434; 和 B&#x1d435;&#xff0c;表示游戲的開始時間和結束時間&#xff0c;以小時為單位。 然后請你計算游戲的持續時間&#xff0c;已知游戲可以在一天開始并在另一天結束&#xff0c;最長持續時間為 2424 小時。 如果 A&#x1d434; 與 B&…

css3 transform的旋轉和位移制作太陽花

css3 transform 實例展示知識點rotate 旋轉translate 位移transform: translate(300px,200px) rotate(90deg) 實例代碼 實例展示 知識點 transform的兩個屬性 rotate 旋轉 translate 位移 transform: translate(300px,200px) rotate(90deg) 實例代碼 <!DOCTYPE html&g…