數據結構與算法(六)分支限界法(Java)

目錄

    • 一、簡介
      • 1.1 定義
      • 1.2 知識回顧
      • 1.3 兩種解空間樹
      • 1.4 三種分支限界法
      • 1.5 回溯法與分支線定法對比
      • 1.6 使用步驟
    • 二、經典示例:0-1背包問題
      • 2.1 題目
      • 2.2 分析
        • 1)暴力枚舉
        • 2)分支限界法
      • 2.3 代碼實現
        • 1)實現廣度優先策略遍歷
        • 2)實現限界函數來剪枝

一、簡介

1.1 定義

分支限界法 是使用 廣度優先策略,依次生成 擴展節點 上的所有分支。就是 把問題的可行解展開,再由各個分支尋找最佳解

分支限界法回溯法 類似,都是 遞歸 + 剪枝,區別在于回溯法使用的是深度優先策略,而分支限界法使用的是廣度優先策略。

1.2 知識回顧

  • 擴展結點: 一個 正在生成兒子 的結點,稱為擴展結點。
  • 活結點: 一個 自身已生成但其兒子還沒有全部生成 的結點,稱為活結點。
  • 死結點: 一個 所有兒子已經全部生成 的結點,稱為死結點。

深度優先策略:

  • 如果對一個擴展結點 R,一旦生成了它的一個兒子 C,就把 C 當作新的擴展結點。
  • 在完成對子樹 C(以 C 為根的子樹)的窮盡搜索之后,將 R 重新變成擴展結點,繼續生成 R 的下一個兒子(如果存在)。

廣度優先策略:

  • 在一個擴展結點變成死結點之前,它一直是擴展結點。

剪枝函數:

剪枝函數:當某個頂點沒有希望,則其所在的樹枝可以減去。

剪枝函數一般有兩種:

  • 約束函數: 剪去不滿足約束條件的路徑。
  • 限界函數: 減去不能得到最優解的路徑。

1.3 兩種解空間樹

子集樹(Subset Trees):

  • 當所給問題是 從 n 個元素的集合中找出滿足某種性質的子集 時,相應的解空間樹稱為 子集樹

Sn 表示 n 結點子樹的數量,在子集樹中,|S0| = |S1| = ... = |Sn-1| = c,即每個結點有相同數目的子樹,通常情況下 c = 2,所以子樹中共有 2^n 個葉子結點。因此,遍歷子集樹的時間復雜度為 O(2^n)

排列樹(Permutation Trees):

  • 當所給問題是 確定 n 個元素滿足某種性質的排列 時,相應的解空間樹稱為排列樹

Sn 表示 n 結點子樹的數量,在排列樹中,通常情況下 |S0| = n, |S1| = n-1, ..., |Sn-1| = 1。所以,排列樹中共有 n! 個葉子結點。因此,遍歷排列樹的時間復雜度為 O(n!)

1.4 三種分支限界法

不同于回溯法,在分支限界法中,每一個活結點只有一次機會成為擴展結點。活結點一旦成為擴展結點,就一次性產生其所有兒子結點,通過 剪枝函數 將導致不可行解或非最優解的兒子結點舍棄,其余 兒子結點被加入活結點表中

然后,從 活結點表 中取下一結點成為當前擴展結點,并重復上述結點擴展過程。這個 過程一直持續到 找到所需解 或 活結點表為空 為止

根據活結點表的形成方式不同分為 三種分支限界法:

  • FIFO分支限界法:活結點表是 隊列,按照隊列先進先出(FIFO)原則選取下一個結點為擴展結點。
  • LIFO分支限界法:活結點表是 堆棧,按照堆棧先今后出(LIFO)原則選取下一個結點為擴展結點。
  • LC分支限界法:活結點是 優先權隊列(Low Cost),按照優先隊列中規定的優先權選取具有最高優先級的活結點成為新的擴展結點。
    • 結點優先權: 在其分支下搜索一個答案狀態需要花費的代價越小,優先級越高。

1.5 回溯法與分支線定法對比

算法名稱對解空間樹的搜索方式存儲結點的常用數據結構結點存儲特性求解目標空間復雜度
回溯法深度優先搜索(DFS)遞歸;
非遞歸時使用堆棧
活結點的所有可行子結點被遍歷后才被從棧中彈出(結點可能多次成為擴展結點)。找出解空間樹中滿足約束條件的所有解。子集樹:O(n)
排列樹:O(n)
分支限界法廣度優先搜索(BFS)
最小消耗優先搜索(LC)
隊列;堆棧、優先隊列每個活結點只有一次成為擴展結點的機會。找出滿足約束條件的一個解,或在滿足約束條件的解中找出某種意義下的最優解。子集樹:O(2^n)
排列樹:O(n!)

1.6 使用步驟

  1. 首先 確定一個合理的限界函數,并 根據限界函數確定目標函數的界
  2. 然后 按照廣度優先策略搜索問題的解空間樹
  3. 在擴展結點處,生成所有兒子結點,估算所有兒子結點對目標函數的可能取值,舍棄不可能通向最優解的結點(剪枝),將其余結點加入到活結點表(隊列/棧)中
  4. 在當前活結點表中,依據相應的分支線定法(FIFO、LIFO、LC),從當前活結點表中選擇一個結點作為擴展結點
  5. 重復 4~3 步,直到 找到所需的解 或 活結點表為空

二、經典示例:0-1背包問題

2.1 題目

假定有N=4件商品,分別用A、B、C、D表示。每件商品的重量分別為 3kg、2kg、5kg和4kg,對應的價值分別為 66元、40元、95元和40元。現有一個背包,可以容納的總重量位 9kg,問:如何挑選商品,使得背包里商品的 總價值最大?

2.2 分析

0-1背包問題,實際上就是排列組合的問題。

1)暴力枚舉

假如我們用A表示物品A存在;A(上劃線)表示物品A不存在。暴力枚舉所有可能的情況如下:

最優解: A 物品+ C 物品 = 161 價值

在這里插入圖片描述

2)分支限界法

首先根據 價值/重量 進行從大到小進行排序,排序結果如下:

  1. 重量:3,價值:66,每份重量價值:22;
  2. 重量:2,價值:40,每份重量價值:20;
  3. 重量:5,價值:95,每份重量價值:19;
  4. 重量:4,價值:40,每份重量價值:10;

假如我們用A表示物品A存在;A(下劃線)表示物品A不存在。解空間樹 如下:

在這里插入圖片描述

首先根據 A 物品的存在情況進行計算,A存在時最優價值為182A不存在時最優價值為155。選擇 最優價值更高的情況A結點

物品列表:A

背包價值:66

最優隊列: A(182)> A(155)

在這里插入圖片描述

彈出 A 結點,再根據 B 物品的存在情況進行計算,B存在時最優價值為182B不存在時最優價值為171。選擇 最優價值更高的情況B結點

物品列表:A、B

背包價值:106

最優隊列: B(182)> B(171)> A(155)

在這里插入圖片描述

彈出 B 結點,再根據 C 物品的存在情況進行計算,C存在時超重×C不存在時最優價值為146。選擇 最優價值更高的情況B結點

物品列表:A

背包價值:66

最優隊列: B(171)> A(155)> C(146)

在這里插入圖片描述

彈出 B 結點,再根據 C 物品的存在情況進行計算,C存在時最優價值為171C不存在時最優價值為106,由于 106 不大于已有最大價值 161,舍棄。選擇 最優價值更高的情況C結點

物品列表:A、C

背包價值:161

最優隊列: C(171)> A(155)> C(146)

在這里插入圖片描述

彈出 C 結點,再根據 D 物品的存在情況進行計算,D存在時超重×D不存在時最優價值為161由于此結點已為葉子結點,退出循環

物品列表:A、C

背包價值:161

在這里插入圖片描述

2.3 代碼實現

為了方便理解,這里分兩步實現:

  • 實現廣度優先策略遍歷;
  • 實現限界函數來剪枝。
1)實現廣度優先策略遍歷
public static void main(String[] args) {Solution solution = new Solution();int[][] arr1 = {{3,66},{2,40},{5,95},{4,40}};long start1 = System.currentTimeMillis();long start2 = System.nanoTime();// 執行程序int result = solution.knapsack(arr1, 9);long end1 = System.currentTimeMillis();long end2 = System.nanoTime();System.out.println(result);System.out.println("耗時:" + (end1 - start1) + " ms," + (end2 - start2) + " ns");
}public int knapsack(int[][] nums, int capacity) {Node rootNode = new Node(0, 0, 0);Queue<Node> queue = new ArrayDeque<>();queue.add(rootNode);int maxValue = 0;while (!queue.isEmpty()) {Node node = queue.poll();if (node.index >= nums.length) {break;}// 左節點:放入背包if (node.bagWeight + nums[node.index][0] <= capacity) {Node newLeftNode = new Node(node.index + 1, node.bagWeight + nums[node.index][0], node.bagValue + nums[node.index][1]);queue.add(newLeftNode);maxValue = Math.max(maxValue, newLeftNode.bagValue);}// 右節點:不放入背包Node newRightNode = new Node(node.index + 1, node.bagWeight, node.bagValue);queue.add(newRightNode);}return maxValue;
}static class Node {/*** 索引(第幾個物品)*/private int index;/*** 背包容量*/private int bagWeight;/*** 背包價值*/private int bagValue;public Node(int index, int bagWeight, int bagValue) {this.index = index;this.bagWeight = bagWeight;this.bagValue = bagValue;}
}
2)實現限界函數來剪枝
public static void main(String[] args) {Solution solution = new Solution();int[][] arr1 = {{3,66},{2,40},{5,95},{4,40}};long start1 = System.currentTimeMillis();long start2 = System.nanoTime();// 執行程序int result = solution.knapsack(arr1, 9);long end1 = System.currentTimeMillis();long end2 = System.nanoTime();System.out.println(result);System.out.println("耗時:" + (end1 - start1) + " ms," + (end2 - start2) + " ns");
}public int knapsack(int[][] nums, int capacity) {// 由于使用了貪心算法,需要先進行排序Arrays.sort(nums, Comparator.comparingDouble(o -> -1.0 * o[1] / o[0]));Node rootNode = new Node(0, 0, 0);// 優先隊列(貪心算法,按照最優價值排序)PriorityQueue<Node> queue = new PriorityQueue<>();queue.add(rootNode);int maxValue = 0;// 遍歷,直到最大最優價值為某一葉子結點while (queue.size() > 0 && queue.peek().index < nums.length) {Node node = queue.poll();// 左節點:放入背包Node newLeftNode = new Node(node.index + 1, node.bagWeight + nums[node.index][0], node.bagValue + nums[node.index][1]);int newLeftUpBound = newLeftNode.getUpBound(nums, capacity);if (newLeftUpBound >= maxValue && newLeftNode.bagWeight <= capacity) {queue.add(newLeftNode);maxValue = Math.max(maxValue, newLeftNode.bagValue);}// 右節點:不放入背包Node newRightNode = new Node(node.index + 1, node.bagWeight, node.bagValue);int newRightUpBound = newRightNode.getUpBound(nums, capacity);if (newRightUpBound >= maxValue) {queue.add(newRightNode);}}return maxValue;
}static class Node implements Comparable<Node> {/*** 索引(例:第 1個物品索引為 1)*/private final int index;/*** 背包容量*/private final int bagWeight;/*** 背包價值*/private final int bagValue;/*** 背包最優價值(上界)*/private int upBound;public Node(int index, int bagWeight, int bagValue) {this.index = index;this.bagWeight = bagWeight;this.bagValue = bagValue;}public int getUpBound(int[][] nums, int capacity) {if (this.upBound > 0) {return this.upBound;}int newUpBound = this.bagValue;int bagLeft = capacity - bagWeight;int i = this.index;while (i < nums.length && bagLeft - nums[i][0] >= 0) {bagLeft -= nums[i][0];newUpBound += nums[i][1];i++;}// 背包未滿,切割后放入if (i < nums.length) {newUpBound += 1.0 * bagLeft / nums[i][0] * nums[i][1];}return this.upBound = newUpBound;}@Overridepublic int compareTo(Node o) {// 倒敘return o.upBound - this.upBound;}
}

整理完畢,完結撒花~ 🌻





參考地址:

1.算法分析與設計:分支限界法,https://blog.csdn.net/weixin_44712386/article/details/105532881

2.(五) 分支限界算法,https://www.jianshu.com/p/538e7612f68d

3.【算法】四、分支限界法,https://blog.csdn.net/m0_64403412/article/details/130694294

4.單源最短路徑問題——分支限界法(Java),https://zhuanlan.zhihu.com/p/601400758

5.java 0-1背包問題 動態規劃、回溯法、分支限界,https://blog.csdn.net/Dl_MrE/article/details/119572322

6.分支限界法求解0/1背包問題動畫演示,https://www.bilibili.com/video/BV1gb411G7FH/

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

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

相關文章

力扣題:字符的統計-12.4

力扣題-12.4 [力扣刷題攻略] Re&#xff1a;從零開始的力扣刷題生活 力扣題1&#xff1a;657. 機器人能否返回原點 解題思想&#xff1a;進行統計即可 class Solution(object):def judgeCircle(self, moves):""":type moves: str:rtype: bool""&qu…

GeoPandas初體驗:它是什么,我用它展示一下shp矢量數據

GeoPandas 是一個開源的 Python 庫&#xff0c;用于處理地理空間數據。它擴展了 Pandas 這個流行的 Python 數據操作庫&#xff0c;增加了對地理數據類型和操作的支持。GeoPandas 結合了 Pandas、Matplotlib 和 Shapely 的功能&#xff0c;提供了一個易于使用且高效的工具&…

c語言實例:計算并輸出一個整數數組的平均值

大家好&#xff0c;今天給大家介紹一個c語言實例&#xff1a;計算并輸出一個整數數組的平均值&#xff0c;文章末尾附有分享大家一個資料包&#xff0c;差不多150多G。里面學習內容、面經、項目都比較新也比較全&#xff01;可進群免費領取。 以下是一個使用C語言實現的實例&am…

Day18——JDK新特性

1.JDK8的新特性 1.1 Lambda表達式 1.1.1 舉例 public class LambdaTest {Testpublic void test1(){Runnable r1 new Runnable() {Overridepublic void run() {System.out.println("test1");}};r1.run();//Lambda表達式的寫法Runnable r2 () ->{System.out.pr…

怎么將用戶引流到你的私域中?

微信私域運營是一種利用微信平臺建立與用戶深度聯系的營銷方式&#xff0c;可在私域中觸達并服務用戶。 那么如何將在將用戶引流至你的私域中呢&#xff1f; 可以從以下幾個小方法入手。 ①打造一個吸引人的個人品牌形象非常重要。在社交媒體上展示真實、獨特、專業的一面&a…

喜訊!云起無垠上榜《成長型初創企業推薦10強》

近期&#xff0c;由中國計算機學會抗惡劣環境計算機專業委員會、信息產業信息安全測評中心和安全牛聯合發起的第十一版《中國網絡安全企業100強》榜單正式發布。在這份備受關注的榜單中&#xff0c;云起無垠憑借其創新的技術能力&#xff0c;榮登《成長型初創企業推薦10強》榜單…

網絡知識學習(筆記三)(傳輸層的TCP)

前面已經介紹了傳輸層的UDP協議的報文以及一下相關的知識點&#xff0c;本次主要是傳輸層的TCP協議&#xff0c;包括TCP報文的詳細介紹&#xff1b;可靠傳輸、流量控制、擁塞控制等&#xff1b;建立連接、釋放連接。 一、TCP基本知識點介紹 1.1、TCP協議的幾個重要的知識點 …

網安領域含金量最高的證書有哪些?看這1篇就足夠了!

文章目錄 一、前言二、CISP三、CISAW四、NISP五、為什么很多人考不下來 一、前言 現在想找網絡安全之類的工作&#xff0c;光有技術是不夠的&#xff0c;還得有東西證明自己&#xff0c;網安三大敲門磚&#xff1a;CTF、漏洞證明和專業證書。 對于CTF的話只是少數人能參加的&…

12月08日,每日信息差

以下是2023年12月08日的12條信息差 第一、英國大宗商品經紀商Marex準備在美國上市 第二、阿里云通義千問登頂HuggingFace排行榜。據了解&#xff0c;HuggingFace的開源大模型排行榜收錄了全球上百個開源大模型&#xff0c;測試維度涵蓋閱讀理解、邏輯推理、數學計算、事實問答…

Gateway:微服務架構中的關鍵組件

Gateway&#xff1a;微服務架構中的關鍵組件 在微服務架構的世界中&#xff0c;Gateway&#xff08;網關&#xff09;扮演著至關重要的角色。它不僅作為流量的入口&#xff0c;還提供路由、鑒權、監控等多種功能。本博客將詳細介紹Gateway的概念、功能以及如何在實際項目中使用…

數據庫基礎概念與范式反范式總結

文章目錄 一、基本概念1、屬性2、元組3、關系4、超鍵5、候選鍵6、主鍵7、主屬性8、外鍵9、函數依賴完全依賴 二、數據庫范式1、第一范式&#xff08;1NF&#xff09;2、第二范式&#xff08;2NF&#xff09;3、第三范式&#xff08;3NF&#xff09;4、巴斯-科德范式&#xff08…

uc_14_IP地址_套接字_字節序轉換

1 計算機網絡 計算機網絡&#xff0c;是指將地理位置不同的具有獨立功能的多臺計算機及其外部設備&#xff0c;通過通信線路連接起來&#xff0c;在網絡操作系統、網絡管理軟件及網絡通信協議的管理和協調下&#xff0c;實現資源共享和信息傳遞的計算機系統。 網絡協議是一種特…

C語言文本模式和二進制模式

前言 本篇文章介紹一下C語言的文本模式和二進制模式 文本文件和二進制文件 從宏觀上看&#xff0c;無論是文本文件還是二進制文件&#xff0c;文件中保存的都是0和1的序列&#xff0c;因為磁盤只有這兩種狀態。不同的文件只是對0、1序列的解釋不同&#xff0c;如果文件內容是…

AtCoder ABC周賽2023 11/4 (Sat) E題題解

目錄 原題截圖&#xff1a; 原題翻譯 題目大意&#xff1a; 主要思路&#xff1a; 代碼&#xff1a; 原題截圖&#xff1a; 原題翻譯 題目大意&#xff1a; 給你一個數組&#xff0c;給你一個公式&#xff0c;讓你選k個元素&#xff0c;用公式算出最終得分。 主要思路&am…

X86匯編語言:從實模式到保護模式(代碼+注釋)--c6

X86匯編語言&#xff1a;從實模式到保護模式&#xff08;代碼注釋&#xff09;–c6 標志寄存器FLAGS&#xff1a; 6th&#xff1a;ZF位&#xff08;Zero Flag&#xff09;&#xff1a;零標志&#xff0c;執行算數或者邏輯運算之后&#xff0c;會將該位置位。10th&#xff1a;D…

安全運營 -- 100個藍隊溯源技巧(逐步更新)

0x00 背景 記錄一些常用的入侵排查命令和日常運維思路分享。(排名不分先后,逐步更新ing) 0x01 linux 查詢所有用戶計劃任務 cat /etc/passwd|cut -f 1 -d : |xargs -I {} crontab -l -u {} 0x02 排查linux記錄密碼后門 strace 監聽ssh來源流量記錄密碼后門(本機輸入的密碼記…

Shell數組函數:數組——數組和循環(三)

數組統計性別 一、定義性別文件 [root192 ~]# vim sex.txt jack m alice f tom m 二、定義腳本統計性別 [root192 ~]# vim sex.sh #!/bin/bash declare -A sex while read line dotypeecho $line | awk {print $2}let sex[$type] done < sex.txtfor i in ${!sex[]} doecho…

Linux基礎——進程初識(一)

1. 硬件 ①馮諾依曼體系 我們常見的計算機&#xff0c;如筆記本。我們不常見的計算機&#xff0c;如服務器&#xff0c;大部分都遵守馮諾依曼體系。其詳細結構如下圖所示 在這里有幾點要說明 1. 這里的儲存器實際上指的是內存 2. 輸入設備與輸出設備都屬于外設 常見的輸入設備…

實現SQL server數據庫完整性

1.創建一個數據庫名為“erp” 主數據文件&#xff1a;初始容量為5MB&#xff0c;最大容量為50MB&#xff0c;遞增量為1MB&#xff0c;其余參數自設。事務日志文件&#xff1a;初始容量為3MB&#xff0c;最大容量為20MB&#xff0c;遞增量為10%&#xff0c;其余參數自設。 創建…

與脾氣不太好的領導,相處原則和相處技巧分享

前言 工作上我看到有的人擅長和各種類型領導相處&#xff0c;而有的人則和領導相處不愉快&#xff0c;不懂靈活變通的人 和領導相處出現沖突時則是當面懟領導&#xff0c;不給領導面子&#xff0c;之后被領導打壓&#xff0c;甚至有的人和領導相處 不和離開等等&#xff0c;…