国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當前位置: 首頁 > news >正文

網(wǎng)站閉站保護網(wǎng)站運營一個月多少錢

網(wǎng)站閉站保護,網(wǎng)站運營一個月多少錢,夜深人不靜免費看高清,網(wǎng)站建設網(wǎng)頁設計做網(wǎng)站一,基本概念 算法簡介 舞蹈鏈算法(Dancing Links,簡稱 DLX)是一種高效解決精確覆蓋問題的算法,實際上是一種數(shù)據(jù)結(jié)構(gòu),可以用來實現(xiàn) X算法,以解決精確覆蓋問題。由高德納(Donald E.…

一,基本概念

算法簡介?

????????舞蹈鏈算法(Dancing Links,簡稱 DLX)是一種高效解決精確覆蓋問題的算法,實際上是一種數(shù)據(jù)結(jié)構(gòu),可以用來實現(xiàn) X算法,以解決精確覆蓋問題。由高德納(Donald E. Knuth)提出 。

精準覆蓋?

????????什么是精確覆蓋(Exact Cover)問題呢?就是在一個全集X中若干子集的集合為S。S* 是 S的一個子集,當且僅當X中的每一個元素在S*中恰好出現(xiàn)一次時,S*稱之為一個精確覆蓋。在計算機科學中,精確覆蓋問題指找出這樣的一種覆蓋,或證明其不存在。這是一個NP-完全問題。

舞蹈鏈

? ? ? ? 其實是一種特殊的數(shù)據(jù)結(jié)構(gòu),用于高效地實現(xiàn)對精確覆蓋問題的求解。它基于雙向循環(huán)鏈表,每個節(jié)點除了包含指向左右節(jié)點的指針外,還包含指向上方和下方節(jié)點的指針,這種結(jié)構(gòu)使得在搜索過程中能夠快速地對鏈表進行插入、刪除和恢復操作。

數(shù)據(jù)結(jié)構(gòu)設計

每個1的節(jié)點包含四個指針:left,?right,?up,?down,形成雙向十字鏈表。

每列有一個列頭節(jié)點,記錄該列中1的數(shù)量(用于優(yōu)化搜索順序)。

算法流程

  1. 選擇列:優(yōu)先選擇當前剩余1最少的列(減少搜索分支)。

  2. 覆蓋列:刪除該列及其關(guān)聯(lián)的所有行(避免后續(xù)搜索沖突)。

  3. 遞歸搜索:對剩余矩陣重復上述步驟。

  4. 回溯恢復:若當前路徑無解,恢復被刪除的列和行,嘗試其他分支。

  5. 結(jié)束條件:當舞蹈鏈中的所有列都被覆蓋(即矩陣中所有列都被刪除)時,找到了一個精確覆蓋解;如果遍歷完所有可能的分支都沒有找到解,則說明該問題無解。

?二,示例

例如,S = {A,B,C,D,E,F} 是全集 X = {1,2,3,4,5,6,7} 的一個子集的集合,其中:

A = {1, 4, 7}

B = {1, 4}

C = {4, 5, 7}

D = {3, 5, 6}

E = {2, 3, 6, 7}

F = {2, 7}

那么,S的一個子集 S* = {B, D, F} 是X的一個精確覆蓋,因為 X 中的每個元素恰好在S*中出現(xiàn)了一次。

可以用0-1矩陣來表示精確覆蓋問題。我們用矩陣的每行表示S的一個元素,也就是X的一個子集;用矩陣的每列表示X的一個元素。矩陣中的1代表這一列的元素存在于這一行對應的子集中,0代表不存在。那么精確覆蓋問題可以轉(zhuǎn)化成求出矩陣若干行的集合,使得集合中的每一列恰好都有一個1。

比如前面的問題可以用矩陣的形式表示成

步驟1

那么選擇紅色的B,D,F能滿足每列都恰好包含一個1。

可以用 Knuth 提出的X算法來解決精確覆蓋問題。X算法是一個非確定性的深度優(yōu)先回溯算法。它的具體步驟如下:

1. 如果矩陣

A

為空(沒有任何列),則當前局部解即為問題的一個解,返回成功;否則繼續(xù)。

2. 根據(jù)一定方法選擇第 c 列。如果某一列中沒有 1,則返回失敗,并去除當前局部解中最新加入的行。

選擇第 r 行,使得

該步是非確定性的

(該步是非確定性的)。

將第 r 行加入當前局部解中。

對于滿足

Ar,j=1

的每一列j,從矩陣

A2

中刪除所有滿足

Ai,j

的行,最后再刪除第 j 列。

對所得比 A 小的新矩陣遞歸地執(zhí)行此算法。

讓我們用 X算法解決上面的精確覆蓋問題。

首先,當前矩陣不為空,算法繼續(xù)進行。那么先選擇1最少的一列。因為 1,2,3,5,6 列都只有 2 個 1,因此我們隨便選擇 1 個,比如第 1 列。

步驟2

行 A 和 B 都含有 1,因此要在這兩行中進行選擇。

先嘗試選擇行 A。將行A加入到當前的解中。

步驟3

行A的 1,4,7 列為 1,根據(jù)第 5 步,需要把所有在 1,4,7 列中含有 1 的行都刪除掉,因此需要刪除掉行A,B,C,E,F,同時刪除掉第 1,4,7 列

步驟4

刪除之后,矩陣只剩下行 D 和第 2,3,5,6 列:

步驟5

進入遞歸,回到第 1 步,矩陣非空,算法繼續(xù)執(zhí)行。

再進入第2步,此時選擇 1 最少的第 2 列,里面沒有 1,因此返回失敗,同時將行 A 從當前的解中移除;

算法進入另一個分支,選擇行 B,并將其加入到當前的解中:

步驟6

行 B 的第 1,4 列為 1,因此要把 1,4 列中包含 1 的行都刪掉。需要刪除掉行 A,B,C,再刪除掉 1,4 列。

步驟7

此時矩陣變?yōu)?/p>

步驟8

進入遞歸,回到第 1 步,矩陣非空,因此算法繼續(xù)。

當前包含 1 最少的一列是第 5 列,那么將從第 5 列中選擇含有 1 的行進行搜索。

步驟9

第 5 列中行 D 含有 1,因此選擇行 D,將其加入當前解中,算法進入新的一層搜索。

步驟10

行 D 的第 3,5,6 列包含 1,我們要刪掉這幾列中包含 1 的所有行,同時刪掉這幾列

步驟11

那么我們需要刪掉行 D,E 和第 3,5,6 列,矩陣變?yōu)?/p>

步驟12

再次遞歸執(zhí)行,回到第 1 步,矩陣非空,因此算法繼續(xù)

選擇當前包含 1 最少的一列,這里選擇第 2 列。第 2 列中只有行 F 包含 1, 因此選擇行 F

將行 F 加入到當前解中,算法進入第 3 層搜索

步驟13

行 F 中第 2,7列為 1,第 2,7 列中行 F 包含 1,因此移除行 F 和第 2,7 列

步驟14

算法再次進入遞歸執(zhí)行,回到第 1 步,此時所有的列都被移除了,矩陣為空,因此返回成功,找到了一個解:{B, D, F}

繼續(xù)搜索,沒有其他可以選擇的行,返回上一層;

第2層也沒有其他可以選擇的行,再返回上一層;

第1層也沒有其他可以選擇的行,再返回上一層;

第0層也沒有其他可以選擇的行,算法終止。

以上就是 X 算法的執(zhí)行過程。Knuth 提出 X 算法主要是為了說明舞蹈鏈的作用,他發(fā)現(xiàn)用舞蹈鏈來執(zhí)行 X 算法效率特別高。那么什么是舞蹈鏈呢?它是基于雙向鏈表的一種數(shù)據(jù)結(jié)構(gòu)。

讓我們先來看看雙向鏈表:

雙向鏈表1

上圖是一個簡單的雙向鏈表,每個節(jié)點有兩個指針,分別指向自己的前驅(qū)和后繼節(jié)點。那么如果我們想把其中一個節(jié)點,比如 B 從鏈表中刪掉,只需要執(zhí)行下面的操作:

B.left.right?=?B.rightB.right.left?=?B.left

注意:此時雖然 B 從鏈表中移除了,但它的兩個指針依然保持不變,還是指向之前的前驅(qū)和后繼節(jié)點。

雙向鏈表2

因此,如果我想把 B 再添加到鏈表原來的位置上,此時并不需要修改 B 的指針,只需要再把 B 的前驅(qū)和后繼節(jié)點的指針恢復就可以了:

B.left.right?=?BB.right.left?=?B

理解了這一點之后,讓我們再來看看舞蹈鏈的結(jié)構(gòu)是怎么樣的:

Dancing links

上面這個圖是一個舞蹈鏈的結(jié)構(gòu),描述的是前面 X 算法中用到的矩陣。它由幾部分構(gòu)成:

最上面的藍色部分是一個水平的環(huán)狀雙向鏈表。最左邊是頭節(jié)點,它是整個數(shù)據(jù)結(jié)構(gòu)的根節(jié)點。其余是列頭節(jié)點,每個代表矩陣中的一列。

每一列又是一個縱向的環(huán)狀雙向鏈表。除了最上面的列頭節(jié)點,其他的每個節(jié)點都代表前面的矩陣中的一個 1。這實際上是一個稀疏矩陣,為了優(yōu)化存儲和效率,只保留了值為 1 的節(jié)點,把每個節(jié)點按順序保存到數(shù)組中。最早的 Dancing Links 算法,也就是 Knuth 在 2000 年發(fā)表的論文中,下面的每一行也都是一個雙向鏈表。但后來他發(fā)現(xiàn)每一行在算法執(zhí)行過程中實際上不會發(fā)生變化,因此他把水平的雙向鏈表取消了,只保留了最頂上的列頭節(jié)點之間的水平雙向鏈表。下面的每一行之間的前后節(jié)點可以直接通過數(shù)組的索引得到。兩邊是Space節(jié)點,用來標記一行的開始和結(jié)束。

每個普通節(jié)點 A 都包含 4 個 字段,A.up 和 A.down 代表雙向鏈表的兩個指針,分別指向 A 上面和下面的節(jié)點。還有一個 A.col ,指向 A 所在列的頭節(jié)點,需要根據(jù)這個字段定位到節(jié)點所在的列。另外還有一個 A.row,主要是方便在遞歸的過程中緩存當前的解。

列頭節(jié)點還要再多幾個字段,left 和 right 分別指向水平雙向鏈表的左節(jié)點和右節(jié)點。另外還有一個 count 字段,代表這一列當前一共有幾個元素。X 算法的第 2 步,選擇 1 最少的列時會用到這個字段。

理解了舞蹈鏈的數(shù)據(jù)結(jié)構(gòu)之后,我們再來看看是怎樣用舞蹈鏈來實現(xiàn) X 算法的。這部分算法很精妙,也是舞蹈鏈這個名字的來由,通過對鏈表上的節(jié)點反復刪除和插入實現(xiàn)了遞歸的回溯,就好像一個個鏈表在舞臺上翩翩起舞一樣。

具體的算法實現(xiàn)可以參照 Knuth 的論文,我們還是用圖的方式來說明一下。

(1)首先,判斷鏈表是否為空,可以通過 head.right == head 來判斷。如果為空則返回,并輸出當前的解。

(2)不為空則選擇當前節(jié)點數(shù)最少的列。如果只有列頭節(jié)點,則返回失敗。

選擇一列

遍歷這一列的每個節(jié)點,開始進行覆蓋操作:

(1)首先將節(jié)點所在行作為解的一部分,加入到當前解中;

選擇列中的一個節(jié)點所在的行

(2)遍歷這一行的所有節(jié)點,將每個節(jié)點所在列都刪除掉,同時刪除掉與這些列有交集的所有行:

2a. 遍歷節(jié)點所在列的每個節(jié)點,將每個節(jié)點所在行的所有節(jié)點從它所在的列中移除掉,同時將列頭節(jié)點的計數(shù)減 1:

node.up.down?=?node.downnode.down.up?=?node.upcol_node.count?-=?1

2b. 還要將這一列從鏈表中移除:

col_node.left.right?=?col_node.rightcol_node.right.left?=?col_node.left

移除了選擇行的所有列,和每一列有交集的所有行

進入遞歸調(diào)用,判斷鏈表是否為空;

不為空則選擇節(jié)點數(shù)最少的列,再遍歷這一列的節(jié)點,進行覆蓋操作:

移除掉所有節(jié)點之后,進入遞歸調(diào)用,發(fā)現(xiàn)鏈表不為空,但節(jié)點數(shù)最少的列中沒有普通節(jié)點了,返回失敗;

開始做鏈表的還原操作。注意還原的順序需要和移除的順序相反。如果我們是從上至下,從左至右移除節(jié)點,那么還原的時候就從右至左,從下至上。否則的話可能會出現(xiàn)問題,導致一個節(jié)點被還原多次,這樣列中節(jié)點的計數(shù)就不準確了。

node.up.down?=?nodenode.down.up?=?nodecol_node.count?+=?1

并且把刪除的列也取消覆蓋

col_node.left.right?=?col_nodecol_node.right.left?=?col_node

遞歸返回到上一層,還原之后,發(fā)現(xiàn)列中沒有其他節(jié)點可以選擇,再返回到上一層,選擇下一個節(jié)點所在的行。

選擇另一個節(jié)點所在行

和之前的方法相同,遍歷這一行的所有節(jié)點,將每個節(jié)點所在列都刪除掉,同時刪除掉與這些列有交集的所有行:

移除了選擇行的所有列,和每一列有交集的所有行

再選擇節(jié)點最少的列,遍歷這一列的所有節(jié)點的所在行:

選擇節(jié)點最少的列,遍歷這一列的節(jié)點所在行

遍歷這一行的所有節(jié)點,刪除掉每個節(jié)點所在列,以及與這些列有交集的所有行:

移除了選擇行的所有列,和每一列有交集的所有行

再次進入遞歸調(diào)用,判斷矩陣不為空,選擇節(jié)點最少的一列,遍歷每個節(jié)點,刪除掉所在行的所有列,與這些列有交集的所有行,最后我們得到一個空矩陣。

空鏈表,只剩頭節(jié)點

此時將得到的解輸出,并返回,接下來還要進行還原操作,然后搜索下一個解。

三、代碼

class Node:def __init__(self):self.left = self.right = self.up = self.down = selfself.column = None  # 列頭節(jié)點self.row = None     # 行標識def solve(matrix):# 構(gòu)建舞蹈鏈head = build_dancing_links(matrix)solution = []search(head, solution)def search(head, solution):if head.right == head:# 找到解,輸出結(jié)果return True# 選擇1最少的列col = choose_column(head)cover(col)# 遍歷該列的每一行row_node = col.downwhile row_node != col:solution.append(row_node.row)# 覆蓋該行關(guān)聯(lián)的所有列right_node = row_node.rightwhile right_node != row_node:cover(right_node.column)right_node = right_node.right# 遞歸搜索if search(head, solution):return True# 回溯solution.pop()left_node = row_node.leftwhile left_node != row_node:uncover(left_node.column)left_node = left_node.leftrow_node = row_node.downuncover(col)return False
class Node:def __init__(self):self.left = self.right = self.up = self.down = selfself.col = self.row = Noneclass DLX:def __init__(self):self.root = Node()self.columns = {}self.answer = []def add_column(self, name):node = Node()node.col = nodenode.row = Nonenode.left = self.root.leftnode.right = self.rootself.root.left.right = nodeself.root.left = nodeself.columns[name] = nodedef add_row(self, row_data):first = Nonelast = Nonefor col_name, value in row_data.items():if value == 1:node = Node()node.col = self.columns[col_name]node.row = row_datanode.up = node.col.upnode.down = node.colnode.col.up.down = nodenode.col.up = nodeif first is None:first = nodeelse:last.right = nodenode.left = lastlast = nodefirst.left = lastlast.right = firstdef cover_column(self, col):col.right.left = col.leftcol.left.right = col.righti = col.downwhile i!= col:j = i.rightwhile j!= i:j.down.up = j.upj.up.down = j.downj = j.righti = i.downdef uncover_column(self, col):i = col.upwhile i!= col:j = i.leftwhile j!= i:j.down.up = jj.up.down = jj = j.lefti = i.upcol.right.left = colcol.left.right = coldef search(self, k):if self.root.right == self.root:print("Solution found:", self.answer)return Truec = self.root.righti = c.downmin_size = float('inf')while i!= c:size = 0j = i.rightwhile j!= i:size += 1j = j.rightif size < min_size:min_size = sizec = ii = i.downself.cover_column(c.col)i = c.downwhile i!= c:self.answer.append(i.row)j = i.rightwhile j!= i:self.cover_column(j.col)j = j.rightif self.search(k + 1):return Trueself.answer.pop()i = i.downj = i.leftwhile j!= i:self.uncover_column(j.col)j = j.leftself.uncover_column(c.col)return False

?運行

# 使用示例
dlx = DLX()
dlx.add_column('C1')
dlx.add_column('C2')
dlx.add_column('C3')
dlx.add_row({'C1': 1, 'C2': 0, 'C3': 1})
dlx.add_row({'C1': 0, 'C2': 1, 'C3': 1})
dlx.add_row({'C1': 1, 'C2': 1, 'C3': 0})
dlx.search(0)

四、算法優(yōu)勢

  • 高效剪枝:通過列頭節(jié)點統(tǒng)計剩余1的數(shù)量,優(yōu)先選擇約束最強的列,大幅減少搜索空間。

  • 快速狀態(tài)恢復:鏈表刪除和恢復的時間復雜度為O(1),回溯代價極低。

  • 通用性:適用于所有可轉(zhuǎn)化為精確覆蓋的問題。

五、應用領(lǐng)域

  • 數(shù)獨求解:數(shù)獨問題可以很自然地轉(zhuǎn)化為精確覆蓋問題,舞蹈鏈算法能夠快速有效地解決數(shù)獨謎題,無論是人工設計的數(shù)獨題目還是大規(guī)模生成數(shù)獨游戲。
  • 計算機視覺:在圖像分割、目標識別等任務中,舞蹈鏈算法可用于解決一些組合優(yōu)化問題,例如將圖像中的像素點精確地劃分到不同的目標區(qū)域。
  • 網(wǎng)絡設計:在網(wǎng)絡拓撲設計、資源分配等方面,舞蹈鏈算法可以幫助找到滿足特定要求的最優(yōu)網(wǎng)絡配置方案,例如在保證網(wǎng)絡連通性的前提下,合理分配網(wǎng)絡設備和鏈路資源。
  • N皇后問題:將棋盤轉(zhuǎn)化為精確覆蓋矩陣。

  • 拼圖游戲:如俄羅斯方塊填充、多米諾骨牌覆蓋等。

總結(jié)

舞蹈鏈算法通過雙向鏈表的動態(tài)調(diào)整,將精確覆蓋問題的搜索效率提升到極致。盡管實現(xiàn)復雜,但它在處理組合優(yōu)化問題時表現(xiàn)卓越,尤其適合約束嚴格的場景。理解其核心在于掌握鏈表操作與回溯思想的結(jié)合。

http://m.aloenet.com.cn/news/45112.html

相關(guān)文章:

  • 向國旗敬禮做時代新人網(wǎng)站上海自動seo
  • 國外做btc的網(wǎng)站軟文推廣文案
  • 自己做家具展示網(wǎng)站優(yōu)化seo深圳
  • 昆明網(wǎng)站建設價目表最新軍事消息
  • 網(wǎng)站建設師百度百科網(wǎng)絡輿情監(jiān)測
  • 石家莊建站網(wǎng)頁模板百度人工服務
  • 源碼建站之網(wǎng)站建設搜索引擎平臺有哪些軟件
  • 做菠菜網(wǎng)站有沒有被騙的百度瀏覽器官方網(wǎng)站
  • 南京微信網(wǎng)站建設百度我的訂單查詢
  • 唐山網(wǎng)站建設費用seo排名公司
  • iis 網(wǎng)站文件被占用seo優(yōu)化多久能上排名
  • 用dw做旅游網(wǎng)站的方法權(quán)重查詢
  • 比較厲害的網(wǎng)站制作公司重慶seo整站優(yōu)化外包服務
  • 淘客做網(wǎng)站怎么備案百度一下百度官網(wǎng)
  • 用國外網(wǎng)站 圖片做自媒體福州seo公司排名
  • 動態(tài)網(wǎng)頁設計個人簡歷代碼seo宣傳網(wǎng)站
  • 銷售seo是什么的
  • 貸款織夢網(wǎng)站模版網(wǎng)絡營銷策略分析論文
  • 國外高校實驗室網(wǎng)站建設成果廣告聯(lián)盟論壇
  • 網(wǎng)站建設的seo策略信息流廣告投放渠道
  • 網(wǎng)站建設高清圖片刷百度關(guān)鍵詞排名優(yōu)化
  • iis 部署wordpressseo刷詞
  • asp網(wǎng)站頁面設計外鏈兔
  • 網(wǎng)站設計外文文獻廣州關(guān)鍵詞排名推廣
  • 微信導購網(wǎng)站怎么做視頻教學百度推廣客服
  • 網(wǎng)站建設 中山自己網(wǎng)站怎么推廣
  • 找做網(wǎng)站的客戶百度統(tǒng)計api
  • vip視頻網(wǎng)站怎么做百度下載安裝到桌面
  • 大連建站公司友情鏈接交易網(wǎng)
  • 網(wǎng)站優(yōu)化排名資源怎樣建立一個網(wǎng)絡銷售平臺