通過一個例子學習回溯算法:從方法論到實際應用

回溯算法:從方法論到實際應用

回溯算法(Backtracking)是一種通過窮舉法尋找問題所有解的算法,它的核心思想是逐步構建解空間樹,在每個步驟中判斷當前解是否合法。如果不合法,就“回溯”到上一步,嘗試其他選擇。回溯算法廣泛應用于組合優化、約束滿足問題、求解排列組合等問題。今天我們將通過一個通俗易懂的例子,深入理解回溯算法的核心思想及其應用。

一、什么是回溯算法?

回溯算法的基本思想是 “試探 + 剪枝”。具體來說,它通過窮舉法逐步構建問題的解,但在每一步,如果發現當前路徑不能繼續下去(即不滿足問題的約束),就會回退到上一步,嘗試其他可能的選擇。

回溯算法通過“剪枝”來減少不必要的計算,從而提高算法的效率。

回溯算法的基本步驟:

  1. 選擇:做出選擇,即決定當前一步的操作。
  2. 約束:判斷選擇是否合法。
  3. 完成:如果達到了問題的終止條件,就保存解并返回。
  4. 回溯:如果當前選擇無解或不能繼續,則撤銷當前選擇,返回到上一步。

二、回溯算法的應用場景

回溯算法適用于那些需要尋找所有解的組合問題,尤其是:

  • 排列問題:如求解全排列。
  • 組合問題:如求解組合數。
  • 約束滿足問題:如八皇后問題、數獨問題。
  • 圖遍歷問題:如深度優先搜索(DFS)等。

今天,我們將以 整數拆分問題 為例,帶你一步步理解回溯算法的使用。

三、通過一個例子理解回溯算法

問題描述:整數拆分

給定一個整數 N N N,我們需要將 N N N拆分為多個正整數之和,并要求拆分的方式不能重復。兩種拆分方式視為相同,如果它們的組成數字相同,只是順序不同。我們的目標是列出所有不重復的拆分方式。

輸入輸出:

  • 輸入:一個整數 N N N
  • 輸出:所有不重復的拆分方式。

例如,對于 N = 6 N = 6 N=6,所有不重復的拆分方式為:

6 = 1 + 1 + 1 + 1 + 1 + 1
6 = 1 + 1 + 1 + 2
6 = 1 + 1 + 3
6 = 1 + 2 + 2
6 = 1 + 5
6 = 2 + 2 + 2
6 = 2 + 4
6 = 3 + 3
6 = 6

解題思路:回溯法

  1. 選擇:每次選擇一個數字 i i i,從 1 到 N N N 之間,加入當前拆分方案。
  2. 約束:加入的數字必須滿足不小于上一個加入的數字(從小到大避免重復)。
  3. 完成:當 N N N 被拆分完(即剩余值為 0),記錄下當前的拆分方案。
  4. 回溯:如果當前方案不合法(剩余值為負),則撤銷選擇,回到上一狀態,繼續嘗試其他數字。

代碼實現:

#include <iostream>
#include <vector>using namespace std;// 回溯函數:n 為當前剩余值,start 為當前可以選擇的最小數字
void findPartitions(int n, int start, vector<int>& current, vector<vector<int>>& results) {// 如果剩余值為 0,保存當前方案if (n == 0) {results.push_back(current);return;}// 從起始值開始嘗試分割for (int i = start; i <= n; ++i) {current.push_back(i); // 添加當前數字到拆分方案findPartitions(n - i, i, current, results); // 遞歸求解剩余部分current.pop_back(); // 回溯}
}int main() {int N;cin >> N;vector<int> current;          // 當前拆分方案vector<vector<int>> results;  // 所有拆分方案// 找到所有不重復的拆分findPartitions(N, 1, current, results);// 按格式輸出結果for (const auto& partition : results) {cout << N << "=";for (size_t i = 0; i < partition.size(); ++i) {cout << partition[i];if (i != partition.size() - 1) cout << "+";}cout << endl;}return 0;
}

代碼解析:

  1. findPartitions 函數:該函數采用回溯的方式生成所有拆分。我們通過遞歸地選擇數字并減去它,直到剩余值為零。當剩余值為零時,表示已經找到一種有效的拆分方式。
  2. start 參數:確保每次遞歸選擇的數字不小于上一次選擇的數字,從而避免生成重復的拆分。
  3. 回溯:當遞歸完成一次選擇后,我們撤銷選擇,即通過 current.pop_back() 將最后一個數字從當前拆分方案中移除。

樣例輸出:

輸入:

6

輸出:

6=1+1+1+1+1+1
6=1+1+1+2
6=1+1+3
6=1+2+2
6=1+5
6=2+2+2
6=2+4
6=3+3
6=6

四、總結回溯算法的關鍵點

  1. 遞歸回溯:回溯算法本質上是遞歸求解問題,在每個步驟選擇合適的候選項,并在后續步驟判斷當前路徑是否能夠繼續。
  2. 剪枝優化:回溯算法并非簡單的窮舉,它通過剪枝避免了很多不必要的計算。例如,在本題中,確保每次選擇的數字不小于上一個選擇的數字,從而避免了重復的拆分方案。
  3. 全局狀態:在遞歸函數中,我們通過一個全局狀態(如當前拆分的數字集合)來維護整個解的過程。

五、回溯算法的應用擴展

回溯算法不僅僅用于整數拆分問題,它還廣泛應用于以下問題中:

  • 八皇后問題:將 8 個皇后放置在一個 8x8 的棋盤上,要求沒有兩個皇后互相攻擊。通過回溯可以高效地求解出所有合法的皇后擺放方式。
  • 數獨問題:通過回溯逐步填充數獨的空格,確保每個數字不違反數獨的規則。
  • 組合問題:例如給定一個集合,求該集合的所有子集,或者從集合中選擇 k 個元素的所有組合。

回溯算法是一種強大的工具,它能夠在解空間樹中高效地找到所有解,同時通過剪枝避免計算無效解。

六、結語

回溯算法通過遞歸和回溯的思想,能夠解決許多組合優化問題,尤其適用于求解所有解、尋找滿足約束的解等問題。理解回溯的思想并能靈活運用,是提升編程能力的一個重要步驟。希望通過本文的講解,你能對回溯算法有更深的理解,并能夠在實際問題中應用這種方法。


謝謝觀看!希望本篇博客能對你學習回溯算法有所幫助!
如果你有任何疑問或想法,歡迎在評論區留言討論!

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

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

相關文章

Python隨機抽取Excel數據并在處理后整合為一個文件

本文介紹基于Python語言&#xff0c;針對一個文件夾下大量的Excel表格文件&#xff0c;基于其中每一個文件&#xff0c;隨機從其中選取一部分數據&#xff0c;并將全部文件中隨機獲取的數據合并為一個新的Excel表格文件的方法。 首先&#xff0c;我們來明確一下本文的具體需求。…

構建樹莓派溫濕度監測系統:從硬件到軟件的完整指南

?作者簡介&#xff1a;2022年博客新星 第八。熱愛國學的Java后端開發者&#xff0c;修心和技術同步精進。 &#x1f34e;個人主頁&#xff1a;Java Fans的博客 &#x1f34a;個人信條&#xff1a;不遷怒&#xff0c;不貳過。小知識&#xff0c;大智慧。 &#x1f49e;當前專欄…

28. Three.js案例-創建圓角矩形并進行拉伸

28. Three.js案例-創建圓角矩形并進行拉伸 實現效果 知識點 WebGLRenderer (WebGL渲染器) WebGLRenderer 是 Three.js 中用于渲染 3D 場景的主要渲染器。 構造器 WebGLRenderer( parameters : Object ) 參數類型描述parametersObject渲染器的配置參數&#xff0c;可選。 …

開源Java快速自測工具,可以調用系統內任意一個方法

java快速測試框架&#xff0c;可以調到系統內任意一個方法&#xff0c;告別寫單測和controller的困擾。 開源地址&#xff1a;https://gitee.com/missyouch/Easy-JTest 我們在開發時很多時候想要測試下自己的代碼&#xff0c;特別是service層或者是更底層的代碼&#xff0c;就…

004 QT常用控件Qwidget_上

文章目錄 前言控件概述QWidgetenable屬性geometry屬性windowTitle屬性windowlcon屬性 小結 前言 本文將會向你介紹常用的Qwidget屬性 控件概述 Widget 是 Qt 中的核心概念. 英文原義是 “?部件”, 我們此處把它翻譯為 “控件” . 控件是構成?個圖形化界面的基本要素. QWi…

Android 好的開源庫

1. 權限請求框架 GitHub - getActivity/XXPermissions: Android 權限請求框架&#xff0c;已適配 Android 14 2. 下載框架 GitHub - lingochamp/okdownload: A Reliable, Flexible, Fast and Powerful download engine.

Flash語音芯片相比OTP語音芯片的優勢

Flash語音芯片和OTP語音芯片是兩種常見的語音解決方案&#xff0c;在各自的應用領域中發揮著重要作用。本文?將介紹Flash語音芯片相比OTP(One-Time Programmable)語音芯片的顯著優勢?。 1?.可重復擦寫?&#xff1a;Flash語音芯片的最大特點是支持多次編程和擦除&#xff0c…

Android命令行工具--dumpsys

dumpsys 是一種在 Android 設備上運行的工具&#xff0c;可提供有關系統服務的信息。可以使用 Android 調試橋 (adb) 從命令行調用 dumpsys&#xff0c;獲取在連接的設備上運行的所有系統服務的診斷輸出。 此輸出通常比您想要的更詳細&#xff0c;因此請使用此頁面上的命令行選…

【深度學習】深刻理解Swin Transformer

Swin Transformer 是一種基于 Transformer 的視覺模型&#xff0c;由 Microsoft 研究團隊提出&#xff0c;旨在解決傳統 Transformer 模型在計算機視覺任務中的高計算復雜度問題。其全稱是 Shifted Window Transformer&#xff0c;通過引入分層架構和滑動窗口機制&#xff0c;S…

從零開始學習 sg200x 多核開發之 sophpi 編譯生成 fip.bin 流程梳理

本文主要介紹 sophpi 編譯生成 fip.bin 流程。 1、編譯前準備 sophpi 的基本編譯流程如下&#xff1a; $ source build/cvisetup.sh $ defconfig sg2002_wevb_riscv64_sd $ clean_all $ build_all $ pack_burn_image注&#xff1a; 需要在 bash 下運行clean_all 非必要可以不…

mysql客戶端命令

目錄 結束符 ; \g \G 中斷輸入 ctrl c 查看命令列表 help ? (\?) connect (\r) status (\s) delimiter (\d) exit (\q) quit (\q) tee (\T) ?編輯 notee (\t) prompt (\R) source (\.) system (\!) ?編輯 use (\u) help contents 結束符 ; \g \G 當我…

scala隱式函數

1 定義 通常我們所說的隱式函數也稱為 隱式轉換&#xff0c;是使用 implicit 修飾的函數 作用&#xff1a; 可以通過一個隱式函數將一種類型轉變為另一種類型 隱式轉換有兩種應用場景&#xff1a; 類型轉換&#xff0c;隱式轉換為期望類型 類型增強 2 示例 ①&#xff1a;類…

Tomcat原理(4)——嘗試手動Servlet的實現

目錄 一、什么是Servlet 1.servlet的定義 2.servlet的結構 二、實現servlet的流程圖 三、具體實現代碼 1、server 2.實體類request&response 3.HttpServlet抽象類 4.再定義三個servlet進行測試 Tomcat原理&#xff08;3&#xff09;——靜&動態資源以及運行項…

Node.js內置模塊

1.內置模塊 Node.js的中文網參考手冊:https://nodejs.cn//api 幫助文檔 API文檔:查看對應的模塊,左邊是模塊,右邊是模塊的成員 源碼:https://github.com/nodejs/node/tree/main/lib 查看 例如: http.js 創建web服務器的模塊 -->進入源碼中,搜索…

【RAG實戰】RAG與大模型應用

1.1 大模型應用的方向&#xff1a;RAG 1.1.1 什么是RAG 1. 生成式AI 一種能夠生成各類內容的技術&#xff0c;包括文本、圖像、音頻和合成數據。自2022年底ChatGPT在全球范圍內推廣以來&#xff0c;基于Transformer解碼器結構的大模型已能在短時間內為用戶生成高質量的文本、…

基于DeepSpeed Chat詳解 PPO 算法中的actor_loss_fn及其核心參數

詳解 PPO 算法中的 actor_loss_fn 及其核心參數 1. 引言 在強化學習中&#xff0c;PPO&#xff08;Proximal Policy Optimization&#xff0c;近端策略優化&#xff09;算法是一種經典且高效的策略優化方法。它通過重要性采樣&#xff08;Importance Sampling&#xff09;和策…

D3 基礎1

D3 D3.js (Data-Driven Documents) 是一個基于 JavaScript 的庫&#xff0c;用于生成動態、交互式數據可視化。它通過操作文檔對象模型 (DOM) 來生成數據驅動的圖形。官方網站是 https://d3js.org/ <!DOCTYPE html> <html lang"en"><head><me…

基線檢查:Windows安全基線.【手動 || 自動】

基線定義 基線通常指配置和管理系統的詳細描述&#xff0c;或者說是最低的安全要求&#xff0c;它包括服務和應用程序設置、操作系統組件的配置、權限和權利分配、管理規則等。 基線檢查內容 主要包括賬號配置安全、口令配置安全、授權配置、日志配置、IP通信配置等方面內容&…

Python -- Linux中的Matplotlib圖中無法顯示中文 (中文為方框)

目的 用matplotlib生成的圖中文無法正常顯示 方法 主要原因: 沒找到字體 進入windows系統的C:\Windows\Fonts目錄, 復制自己想要的字體 粘貼到Linux服務器中對應python文件所處的文件夾內 設置字體: 設置好字體文件的路徑在需要對字體設置的地方設置字體 效果 中文正常顯…

快速理解類的加載過程

當程序主動使用某個類時&#xff0c;如果該類還未加載到內存中&#xff0c;則系統會通過如下三個步驟來對該類進行初始化&#xff1a; 1.加載&#xff1a;將class文件字節碼內容加載到內存中&#xff0c;并將這些靜態數據轉換成方法區的運行時數據結構&#xff0c;然后生成一個…