中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

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

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

一,基本概念

算法簡介?

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

精準(zhǔn)覆蓋?

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

舞蹈鏈

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

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

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

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

算法流程

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

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

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

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

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

?二,示例

例如,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的一個精確覆蓋,因?yàn)?X 中的每個元素恰好在S*中出現(xiàn)了一次。

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

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

步驟1

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

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

1. 如果矩陣

A

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

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

選擇第 r 行,使得

該步是非確定性的

(該步是非確定性的)。

將第 r 行加入當(dāng)前局部解中。

對于滿足

Ar,j=1

的每一列j,從矩陣

A2

中刪除所有滿足

Ai,j

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

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

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

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

步驟2

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

先嘗試選擇行 A。將行A加入到當(dāng)前的解中。

步驟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

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

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

算法進(jìn)入另一個分支,選擇行 B,并將其加入到當(dāng)前的解中:

步驟6

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

步驟7

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

步驟8

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

當(dāng)前包含 1 最少的一列是第 5 列,那么將從第 5 列中選擇含有 1 的行進(jìn)行搜索。

步驟9

第 5 列中行 D 含有 1,因此選擇行 D,將其加入當(dāng)前解中,算法進(jìn)入新的一層搜索。

步驟10

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

步驟11

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

步驟12

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

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

將行 F 加入到當(dāng)前解中,算法進(jìn)入第 3 層搜索

步驟13

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

步驟14

算法再次進(jìn)入遞歸執(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é)點(diǎn)有兩個指針,分別指向自己的前驅(qū)和后繼節(jié)點(diǎn)。那么如果我們想把其中一個節(jié)點(diǎn),比如 B 從鏈表中刪掉,只需要執(zhí)行下面的操作:

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

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

雙向鏈表2

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

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

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

Dancing links

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

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

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

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

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

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

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

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

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

選擇一列

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

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

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

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

2a. 遍歷節(jié)點(diǎn)所在列的每個節(jié)點(diǎn),將每個節(jié)點(diǎn)所在行的所有節(jié)點(diǎn)從它所在的列中移除掉,同時將列頭節(jié)點(diǎn)的計(jì)數(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

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

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

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

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

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

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é)點(diǎn)可以選擇,再返回到上一層,選擇下一個節(jié)點(diǎn)所在的行。

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

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

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

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

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

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

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

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

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

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

三、代碼

class Node:def __init__(self):self.left = self.right = self.up = self.down = selfself.column = None  # 列頭節(jié)點(diǎn)self.row = None     # 行標(biāo)識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

?運(yùn)行

# 使用示例
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é)點(diǎn)統(tǒng)計(jì)剩余1的數(shù)量,優(yōu)先選擇約束最強(qiáng)的列,大幅減少搜索空間。

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

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

五、應(yīng)用領(lǐng)域

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

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

總結(jié)

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

http://www.risenshineclean.com/news/45112.html

相關(guān)文章:

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