跟著Carl學算法--回溯【2】

IP復原(難)

力扣鏈接:IP復原

題目:有效 IP 地址 正好由四個整數(每個整數位于 0255 之間組成,且不能含有前導 0),整數之間用 '.' 分隔。

  • 例如:"0.1.2.201" "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245""192.168.1.312""192.168@1.1" 是 無效 IP 地址。

給定一個只包含數字的字符串 s ,用以表示一個 IP 地址,返回所有可能的有效 IP 地址,這些地址可以通過在 s 中插入 '.' 來形成。你 不能 重新排序或刪除 s 中的任何數字。你可以按 任何 順序返回答案。

思路
回溯三問{當前操作?枚舉分隔符‘.’的位置(有三種情況,長度1,2,3)子問題?從下標≥j的后綴中繼續修復IP下一個子問題?從下標≥j+1的后綴中繼續修復IP回溯三問\left\{\begin{array}{l}\text {當前操作?枚舉分隔符‘.’的位置(有三種情況,長度1,2,3)} \\ \text {子問題?從下標} \geq j \text {的后綴中繼續修復IP} \\ \text {下一個子問題?從下標} \geq j+1\text {的后綴中繼續修復IP}\end{array}\right. 回溯三問????當前操作?枚舉分隔符‘.’的位置(有三種情況,長度123子問題?從下標j的后綴中繼續修復IP下一個子問題?從下標j+1的后綴中繼續修復IP?
判斷IP合法,即所枚舉分割的字符串(從i開始,長度枚舉)

  1. 開頭不為0(注意,這里是當長度大于1才有的約束,單單一個0完全可以,01就不行)
  2. 轉換成的數字小于等于255

邊界情況

最后可能剩余長度不到3了

比如長度為4 ,i為3,j只能為1,即i+j>n會越界

注意這里:substr(i,j),這里從下標為i的開始,長度為j,i為3說明前面有三個元素,所以i+j>n時才越界,而不是i+j≥n就越界

這里使用了vector<string>來定義路徑,而不是string,優點有兩個

  • 可以在最后統一處理“.”,而不是每次加入path時就+”.“,不需要在最后一步額外再處理”.“
  • 回溯方便,pop()即可,不需要使用earse函數

stoi(),C++內置字符串轉整型

class Solution {
public:vector<string> restoreIpAddresses(string s) {int n = s.size();vector<string> result;vector<string> path;if (n < 4 || n > 12)return {};auto dfs = [&](this auto&& dfs, int i) {if (i == n && path.size() == 4) {result.emplace_back(path[0] + '.' + path[1] + '.' + path[2] +'.' + path[3]);return;}// 剪枝函數,如果剩余字符數小于剩余段數,或者剩余字符數大于剩余段數乘以3,則不可能構成有效的IP地址if (n - i < 4 - path.size() || n - i > 3 * (4 - path.size()))return;for (int j = 1; j <= 3; j++) {if (i + j >n)break;string sub = s.substr(i, j);if (sub[0] == '0' && j > 1) //continue;if (std::stoi(sub) > 255)continue;path.emplace_back(sub);dfs(i + j);path.pop_back();}};dfs(0);return result;}
};

全排列

力扣鏈接:全排列

題目:給定一個不含重復數字的數組 nums ,返回其 所有可能的全排列 。你可以 按任意順序 返回答案。

思路
回溯三問{當前操作?枚舉nums中未使用過的元素子問題?構造path的≥i+1的部分,剩余元素為{nums}-{x1}下一個子問題?繼續構造path的≥i+1的部分,剩余元素為{nums}-{x2}回溯三問\left\{\begin{array}{l}\text {當前操作?枚舉nums中未使用過的元素} \\ \text {子問題?構造path的≥i+1的部分,剩余元素為\{nums\}-\{x1\}} \\ \text {下一個子問題?繼續構造path的≥i+1的部分,剩余元素為\{nums\}-\{x2\}}\end{array}\right. 回溯三問????當前操作?枚舉nums中未使用過的元素子問題?構造path≥i+1的部分,剩余元素為{nums}-{x1}下一個子問題?繼續構造path≥i+1的部分,剩余元素為{nums}-{x2}?
image-20250304194326082

與之前的組合不同,這道題可以往回遍歷,比如【1,2,3】,當遍歷到元素2時,只要元素1未使用就可以接著添加到中,【1,2】,【2,1】是不同的答案,因此整體的不同之處(從答案的角度看)

  • 每次選擇路徑元素都要將nums整體從頭遍歷一遍

  • 需要一個used保存已經使用過的元素,保證從頭遍歷時不會出現重復

如何選擇去重集合used,直接使用used結合去重?自定義標記使用元素

錯誤寫法,使用增強for循環時只能遍歷,不能修改集合,迭代器指針指向會出現問題

for (int i:unused) { //for增強循環path.push_back(i);unused.erase(i);//修改集合dfs();unused.insert(i);//修改集合
}

現在就存在了問題,要遍歷就不能實現回溯,不能同時實現

所以:只能使用自定義標記 bool數組false或true區別

class Solution {
public:vector<vector<int>> permute(vector<int>& nums) {vector<int> path;vector<vector<int>> result;bool used[6] = {false};int n = nums.size();auto dfs = [&](this auto&& dfs, int i) {if (i == n) {result.push_back(path);return;}for (int j = 0; j < n; j++) {if (used[j]) //使用過的元素不在使用continue;path.push_back(nums[j]);used[j] = true;dfs(i + 1);//回溯時記得連同使用過的元素一起回溯path.pop_back();used[j] = false;}};dfs(0);return result;}
};

全排列II

力扣鏈接:全排列II

題目:給定一個可包含重復數字的序列 nums按任意順序 返回所有不重復的全排列。

相較于全排列nums中有重復元素了
回溯三問{當前操作?枚舉nums中未使用過,且本層未使用過的元素子問題?構造path的≥i+1的部分,剩余元素為{nums}-{x1}下一個子問題?繼續構造path的≥i+1的部分,剩余元素為{nums}-{x2}回溯三問\left\{\begin{array}{l}\text {當前操作?枚舉nums中未使用過,\textcolor{red}{且本層未使用過的元素}} \\ \text {子問題?構造path的≥i+1的部分,剩余元素為\{nums\}-\{x1\}} \\ \text {下一個子問題?繼續構造path的≥i+1的部分,剩余元素為\{nums\}-\{x2\}}\end{array}\right. 回溯三問????當前操作?枚舉nums中未使用過,且本層未使用過的元素子問題?構造path≥i+1的部分,剩余元素為{nums}-{x1}下一個子問題?繼續構造path≥i+1的部分,剩余元素為{nums}-{x2}?
思路

思考:上一道題與非遞減子序列的結合

只需要在上一道題的基礎上加個本層去重即可,

應該?排序?+?相同?元素?跳過?或者?每?層?s?et?集合去重均可🤔

這里使用set集合去重

即bool數組用于避免同一個元素使用兩次,set集合用于避免出現同一種排列(本層同一種(不是同一個元素選兩次)

class Solution {
public:vector<vector<int>> permuteUnique(vector<int>& nums) {vector<vector<int>> result;vector<int> path;bool used[8] = {false}; // 此元素使用過int n = nums.size();auto dfs = [&](this auto&& dfs, int i) {if (i == n) {result.push_back(path);return;}unordered_set<int> curUsed; // 相等元素本層使用過for (int j = 0; j < n; j++) {if (used[j] || curUsed.find(nums[j]) != curUsed.end())continue;path.push_back(nums[j]);used[j] = true;curUsed.insert(nums[j]);dfs(i + 1);//回溯,因為set記錄的是本層的重復元素,所以不需要回溯path.pop_back();used[j] = false;}};dfs(0);return result;}
};

N皇后

力扣鏈接:N皇后

題目:按照國際象棋的規則,皇后可以攻擊與之處在同一行或同一列或同一斜線上的棋子。

n 皇后問題 研究的是如何將 n 個皇后放置在 n×n 的棋盤上,并且使皇后彼此之間不能相互攻擊。

給你一個整數 n ,返回所有不同的 n 皇后問題 的解決方案。

每一種解法包含一個不同的 n 皇后問題 的棋子放置方案,該方案中 'Q''.' 分別代表了皇后和空位。

img

思路

與全排列類似,是枚舉列號的全排列,舉例,即[1,2,3,4]的全排列,數組的下標代表行號,內容代表列號,

在此基礎上加了兩個難點

  1. 最終結果的輸出[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
    • 遍歷保存了每一行queen位置的queens數組,queen在第幾列,就構造一個n個"."的字符串,同時構造一個n-1-queen(加起來一共是n個字符)位置個“.”字符串,兩者通過一個Q進行拼接,即為答案
  2. 每一組全排列要額外滿足不在對角線上
    • 很巧妙很巧妙,復制的靈神的
    • 首先對角線分為兩類,主對角線和副對角線,需要構造兩個數組分別存儲
    • 其次,對于同一條對角線,
      • 副對角線(左下到右上):行數 +列數(r+c)相等,想象一下,r每-1,c就+1。
        范圍【0,n*2-2】,因為r【0,n-1】,c【0,n-1】,兩者相加的范圍就是【0,2*n-2】
      • 主對角線(左上到右下):行數 - 列數(r-c)相等,因為,同一條對角線上 r每+1,c就+1。
        范圍【-(n-1),n-1】,因為r【0,n-1】,-c【-(n-1),0】,兩者相加的范圍就是【-(n-1),n-1】
    • 基于以上思考,可以兩者均定義為n*2-1的數組,副對角線計算時直接r+c即可,主對角線計算時為避免負索引,需要r-c+n-1

處理完這兩個難點,這道題就和全排列的解決步驟相同了

class Solution {
public:vector<vector<string>> solveNQueens(int n) {vector<vector<string>> ans;vector<int> queens(n); // queens 數組用于存儲皇后的位置,queens[r] 表示第 r 行的皇后放在了第 queens[r] 列//這里標記位置的col,diag本來bool類型即可,但是靈神說int效率更高vector<int> col(n);// col 數組用于標記每一列是否已經被占用vector<int> diag1(n * 2 - 1); // diag1 數組用于標記副對角線(左下到右上)是否已經被占用// r + c 的值在同一條副對角線上是相同的vector<int> diag2(n * 2 - 1); // diag2 數組用于標記主對角線(左上到右下)是否已經被占用// r - c + n - 1 的值在同一條主對角線上是相同的auto dfs = [&](this auto&& dfs, int r) {//結束if (r == n) {//將queens數組生成字符串,并整合為答案vector<string> board(n);for (int i = 0; i < n; i++) {// 使用字符串構造函數生成一行,先生成 queens[i] 個 '.',然后是 'Q',最后是 n - 1 - queens[i] 個 '.board[i] = string(queens[i], '.') + 'Q' + string(n - 1 - queens[i], '.');}ans.push_back(board);return;}// 在第 r 行的每一列嘗試放置皇后for (int c = 0; c < n; c++) {// 計算當前位置所在的副對角線的索引int rc = r - c + n - 1;// 如果當前列和兩條對角線都沒有被占用,說明可以在當前位置放置皇后if (!col[c] && !diag1[r + c] && !diag2[rc]) { //!col[c]即為與全排列的元素不能重復的邏輯實現queens[r] = c;// 標記當前列和兩條對角線已經被占用col[c] = diag1[r + c] = diag2[rc] = true;// 遞歸調用 dfs,嘗試在下一行放置皇后dfs(r + 1);// 恢復現場,回溯到上一步,嘗試在其他位置放置皇后col[c] = diag1[r + c] = diag2[rc] = false;}}};dfs(0);return ans;}
};

解數獨

力扣鏈接:解數獨

題目:編寫一個程序,通過填充空格來解決數獨問題。

數獨的解法需 遵循如下規則

  1. 數字 1-9 在每一行只能出現一次。
  2. 數字 1-9 在每一列只能出現一次。
  3. 數字 1-9 在每一個以粗實線分隔的 3x3 宮內只能出現一次。(請參考示例圖)

數獨部分空格內已填入了數字,空白格用 '.' 表示。

image-20250305194744742

思路

難點:

  1. 當前位置枚舉數字的合法性檢測
  2. 二維遞歸

合法性檢測:3X3范圍內只出現一次?

可以先將范圍定位到就九個中棋盤,即row/3定位到縱向三個位置中的一個,同理col也是,0,即代表前面有0個粗化的行(即3個細行),其他同理
定位到中位置后,再進一步將大體位置細化到具體的起始行和列,將原來的大體位置*3即可,因為一個行號或者列號對應三個細化的行和列,

以行為例,【0,1,2】分別即對應【0,3,6】

然后知道起始行也知道長度,遍歷九宮格即可

for (int i = startRow; i < startRow + 3; i++) { // 判斷9方格里是否重復for (int j = startCol; j < startCol + 3; j++) {if (board[i][j] == val) {return false;}}}

二維遞歸

要始終明白,下一個子問題是從填充后面所有位置,而不是填充從下一行開始到結束的所有位置

名稱一樣二維遞歸

auto dfs = [&](this auto&& dfs, int row) -> bool {for(int i = row; i < 9; i++)  //(上一層填充了一個位置)從當前行開始重新遍歷,更好的做法是直接定位到某行某列,//但是不易于實現和理解for (int j = 0; j < 9; j++) {if (board[i][j] == '.') {for (char num = '1'; num <= '9'; num++) {if (isValid(i, j, num, board)) {board[i][j] = num;if (dfs(i))  //深入判斷,只有此處一直為真才會返回真,//當路徑偏差時(即上一層為真,但是導致下一層所有數字都不可填入時),會返回假return true;board[i][j] = '.';}}//無論前面是否填充,進行到這一步就返回false,//當前面九個數字都不滿足時返回 false//一旦有數字滿足就會深入分支,進行進一步深入判斷,return false;}}//遍歷結束,直接返回真即可,只要遍歷到最后,說明前面都符合,//中途一個位置出現了所有數字都不能填充當前位置時(即填充行為無法進行時),就會return falsereturn true;};

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

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

相關文章

PyTorch生成式人工智能(17)——變分自編碼器詳解與實現

PyTorch生成式人工智能(17)——變分自編碼器詳解與實現 0. 前言1. 潛空間運算2. 變分自編碼器2.1 自編碼器與變分自編碼器對比2.2 模型訓練流程3. 構建變分自編碼器3.1 模型構建3.2 模型訓練3.3 生成圖像4. 向量運算小結系列鏈接0. 前言 雖然自編碼器 (AutoEncoder, AE) 在重…

SpringMVC2

一、接口聲明的穩定性- 接口聲明不能輕易變&#xff1a;接口是前后端、服務間通信的約定。要是接口的 URL、請求方法、參數、返回值變了&#xff0c;調用方&#xff08;比如前端、其他服務&#xff09;就得跟著改&#xff0c;容易出問題。所以設計接口要謹慎&#xff0c;別老變…

LVS集群實踐

一、LVS概念VS: Virtual Sever &#xff08;調度器&#xff09;RS: Real Sever &#xff08;資源主機&#xff09;CIP: Client IP &#xff08;用戶IP&#xff09;VIP: Virtual sever IP &#xff08;VS外網的IP&#xff0c;客戶訪問的IP&#xff09;DIP: Director IP &#xf…

使用Django框架構建Python Web應用

前言Django個高級Python Web框架&#xff0c;遵循MTV&#xff08;Model-Template-View&#xff09;設計模式&#xff1a;模型(Model)&#xff1a;數據層&#xff0c;定義數據結構模板(Template)&#xff1a;表現層&#xff0c;處理用戶界面視圖(View)&#xff1a;業務邏輯層&am…

[AI-video] 數據模型與架構 | LLM集成

第五章&#xff1a;數據模型與架構 歡迎來到第五章&#xff01; 在前幾章中&#xff0c;我們學習了網頁用戶界面&#xff08;UI&#xff09;&#xff08;控制面板&#xff09;、應用配置&#xff08;系統參數設置&#xff09;、任務編排&#xff08;視頻生成流程的總調度&…

HTTP 性能優化實戰:突破高并發瓶頸的工業級方案

在互聯網高并發場景中&#xff0c;HTTP 性能表現直接決定系統生死。當每秒請求量突破十萬級甚至百萬級時&#xff0c;哪怕 100 毫秒的延遲都會引發用戶流失、交易失敗等連鎖反應。本文基于五大行業實戰案例&#xff0c;拆解 HTTP 性能瓶頸的底層邏輯&#xff0c;輸出可直接落地…

Xsens人形機器人擬人動作AI訓練,提升機器人工作精度與效率

隨著人工智能與機器人技術的深度融合&#xff0c;人形機器人正從實驗室走向工業制造、醫療護理、公共服務等真實場景。然而&#xff0c;要讓機器人真正"像人類一樣工作"&#xff0c;其動作的流暢性、精準度與環境適應性仍是技術突破的關鍵。Xsens動作捕捉系統通過創新…

IIS網站間歇性打不開暴力解決方法

背景 網站使用 Asp.NET 框架開發&#xff0c;使用 SQL Server 2012 IIS 8.5 運行。開發上線以后&#xff0c;經常出現網站間歇性打不開&#xff0c;但是重啟 IIS 就可以正常訪問。 問題排查過程 打開日志記錄 觀察 CPU&#xff0c;內存&#xff0c;帶寬流量等占用正常&#xf…

JavaScript 動態訪問嵌套對象屬性問題記錄

問題描述不能解析 2 層 只能解析一層在 Vue 項目中&#xff0c;嘗試通過動態路徑&#xff08;如 otherInfo.businessPlacePhotoUrlLabel&#xff09;訪問或修改嵌套對象屬性時&#xff0c;發現 this[a.b.c] 無法正確解析&#xff0c;導致返回 undefined。錯誤示例removeImg(val…

7.17 滑動窗口 | assign

lc3015.法1&#xff1a;暴力bfs&#xff0c;數據范圍only 100&#xff0c;可以過法2&#xff1a;加入了x,y&#xff0c;可以思考加入的x,y影響了什么呢? 通過數學找規律class Solution { public:vector<int> countOfPairs(int n, int x, int y) {vector<int> ret(…

預訓練模型:大規模數據預學習范式——定義、原理與演進邏輯

本文由「大千AI助手」原創發布&#xff0c;專注用真話講AI&#xff0c;回歸技術本質。拒絕神話或妖魔化。搜索「大千AI助手」關注我&#xff0c;一起撕掉過度包裝&#xff0c;學習真實的AI技術&#xff01; 以下基于權威教材、學術論文及行業技術報告&#xff0c;對“預訓練模型…

【kubernetes】--安全認證機制

文章目錄安全認證1. **身份認證&#xff08;Authentication&#xff09;**2. **授權&#xff08;Authorization&#xff09;**3. **準入控制&#xff08;Admission Control&#xff09;**4. **機密信息管理**5. **其他安全實踐**安全認證 Kubernetes 的安全機制覆蓋了從身份驗…

扣子工作流詳解

《扣子開發AI Agent智能體應用&#xff08;人工智能技術叢書&#xff09;》(宋立桓&#xff0c;王東健&#xff0c;陳銘毅&#xff0c;程東升)【摘要 書評 試讀】- 京東圖書 《扣子開發AI Agent智能體應用》案例重現 開發agent智能體的書籍-CSDN博客 工作流是指一系列相互關聯…

【一文解決】塊級元素,行內元素,行內塊元素

塊級元素&#xff0c;行內元素&#xff0c;行內塊元素&#xff01;盒模型1.標準盒模型&#xff08;box-sizing: content-box&#xff09;2.IE 盒模型&#xff08;box-sizing: border-box&#xff09;&#xff01;margin & padding1.margin、padding是什么2. 應用一、塊級元…

在 Spring Boot 中使用 MyBatis 的 XML 文件編寫 SQL 語句詳解

前言 在現代 Java Web 開發中&#xff0c;Spring Boot 和 MyBatis 是兩個非常流行的技術框架。它們的結合使得數據庫操作變得更加簡潔和高效。本文將詳細介紹如何在 Spring Boot 項目中使用 MyBatis 的 XML 文件來編寫 SQL 語句&#xff0c;包括配置、代碼結構、SQL 編寫技巧以…

字段級權限控制場景中,RBAC與ABAC的性能差異

RBAC(基于角色訪問控制)與ABAC(基于屬性訪問控制)的性能差異主要體現在??計算復雜度、策略靈活性、擴展性??和??資源消耗??等方面。以下是具體對比分析: ??一、性能對比維度?? ??維度????RBAC????ABAC????計算復雜度??低(預計算角色權限映射…

Reddit Karma是什么?Post Karma和Comment Karma的提升指南

在Reddit這一用戶活躍度高的社區里&#xff0c;想要獲得更好的曝光&#xff0c;我們就需要提升我們的Karma值&#xff0c;什么是Reddit Karma&#xff1f;怎么樣才能提升以獲得更大的影響力&#xff1f;本文將為你提高一套切實可行的提升方案。一、什么是Reddit Karma&#xff…

基于Canal實現MySQL數據庫數據同步

一、基礎概念與原理 1. Canal是什么&#xff1f; 阿里巴巴開源的MySQL binlog增量訂閱與消費組件&#xff0c;通過偽裝為MySQL Slave監聽Master的binlog變更&#xff0c;實現實時數據同步。 Canal 官方網站&#xff1a;https://github.com/alibaba/canal Canal Demo&#x…

算法第23天|貪心算法:基礎理論、分發餅干、擺動序列、最大子序和

今日總結&#xff1a; 擺動序列的三種特殊情況需要著重思考&#xff0c;感覺是沒有思考清楚 基礎理論 1、貪心的本質&#xff1a; 貪心的本質是選擇每一階段的局部最優&#xff0c;從而達到全局最優。 例如&#xff1a;一堆鈔票&#xff0c;只能拿走10張&#xff0c;如何拿走最…

Q-chunking——帶有動作分塊的強化學習:基于人類演示,進行一定的連貫探索(且可做到無偏的n步價值回溯)

前言 我在之前的文章中提到過多次&#xff0c;長沙具身團隊是我司建設的第二支具身團隊&#xff0c;通過5月份的全力招聘&#xff0c;為了沖刺6月底和7月初來長沙辦公室考察的第一批客戶&#xff0c;過去一個多月來&#xff0c;長沙分部(一開始就5人&#xff0c;另外5人 實習…