dancing links解決X問題的C 實現

2021-07-07 04:47:53 字數 3275 閱讀 9963

x問題,也稱精確覆蓋問題,就是給定乙個01矩陣,需要從中選取一些行組成乙個子矩陣,這個子矩陣的每一列有且僅有乙個1。這個問題聽起來就知道很難,必須使用回溯演算法來解決,但是我們知道回溯演算法要提高效率,就必須做好剪枝和回溯恢復的工作。這時演算法大師donald e.knoth給出了乙個巧妙的資料結構,十字鍊錶,每個節點都有上下左右的指標。其實這個結構參考的是雙鏈表刪除和恢復的便利性,思考一下,在雙鏈表中,刪除乙個節點的**就是n->left->right=n->right; n->right->left=n->left,而撤銷刪除的**就是n->left->right=n; n->right->left=n;這幾乎不需要任何代價就能解決。舉個例子,乙個如下的01矩陣

board.push_back("0010110");

board.push_back("1001001");

board.push_back("0110010");

board.push_back("1001000");

board.push_back("0100001");

board.push_back("0001101");

利用十字鍊錶就表示如下:

其中第一行是特殊的節點,我們可以稱為列節點,第乙個是頭節點,他們都是不儲存資料,然後其他節點就是矩陣中的1,為了除錯方便,把它們的節點資料都寫為節點序號,說了那麼多,都忘記給出節點的資料結構了:

struct cell

};

細心的讀者肯定會發現,節點結構中除了上下左右指標外,還有乙個col指標,沒錯,這個是指向每一列的列節點的指標。說了這裡,也應該開始講演算法思路了,

1.取出nextcell=head->right,如果nextcell等於head,則演算法結束,返回答案,否則進入第2步。

2.刪除nextcell,遍歷nextcell下面的所有節點,將這些節點所在的行都刪除。

4.遍歷downcell右邊的所有節點,將每個節點的列節點都按照第2步的方法刪除,然後重新進入第1步。

5.回溯恢復到刪除downcell所有右節點的列節點的狀態,然後downcell=downcell->down,如果downcell等於nextcell,證明也不能回溯了,需要恢復nextcell,繼續返回上一級恢復,否則進入第4步。

演算法的思路就只有這麼多了,其中恢復節點看上去很難,但只要按照刪除的順序逆回去,堆疊就會很好地幫你實現了。具體刪除和恢復的**如下:

bool removecolcell(cell* colcell)

downcell=downcell->down;

} return true;

}void recovercolcell(cell* colcell)

downcell=downcell->down;

} colcell->left->right=colcell;

colcell->right->left=colcell;

}

dlx演算法主體**如下:

bool solvewithdlx(cell* head,vector& answers)

if(solvewithdlx(head,answers))else

downcell=downcell->down;

} }recovercolcell(nextcell);

return false;

}

然後構建十字鍊錶的**如下:

vectorboard;

board.push_back("0010110");

board.push_back("1001001");

board.push_back("0110010");

board.push_back("1001000");

board.push_back("0100001");

board.push_back("0001101");

cell* head=new cell(0);

cell* lastcell=head;

vectorcolumncells;

for(int i=0;i<7;i++)

lastcell->right=head;

head->left=lastcell;

int index=1;

for(int i=0;iup=columncell->up;

cell->down=columncell;

columncell->up->down=cell;

columncell->up=cell;

cell->col=columncell;

if(lastcell==null)

cell->left=lastcell;

lastcell->right=cell;

lastcell=cell;

}} firstcell->left=lastcell;

lastcell->right=firstcell;

} vectoranswers;

solvewithdlx(head,answers);

for(int i=0;i

最後的最後,雖然上面的**解決一般的x問題沒問題,但是當我將數獨問題轉化成x問題時,再用dlx演算法卻始終沒跑出來,還請各位大神幫忙看一眼,其中刪除和恢復的**都是一樣的,只是構建十字鍊錶不太一樣,這個十字鍊錶一共有324列,也就是81*4,第乙個81是代表81個格仔中第幾個格仔有數,第二個81是代表第幾行有哪個數字,第三個81是代表第幾列有哪個數字,第四列是代表第幾個九宮格有哪個數字,舉個例子,如果第2個格仔是9,那麼在十字鍊錶中將插入一行,這一行在第1列是1,第81+8列是1,第81*2+9+8列是1,以及第81*3+8列是1,而如果第2個格仔是空的,那麼這個格仔會有9種可能,所以會插入9行,第0行中第1列是1,第81+0列是1,第81*2+9+0列是1,第81*3+0是1,其他行也如此類推。**如下:

void solvesudoku(vector>& board)

lastcell->right=head;

head->left=lastcell;

for(int i=0;i<9;i++)else

}} }

vectoranswers;

solvewithdlx(head,answers);

for(int i=0;i

鳴謝給出了大量的圖和詳細的講解。

DANCING LINKS解決重複覆蓋問題

問題描述 給定乙個n m的矩陣,有些位置為1,有些位置為0。如果g i j 1則說明i行可以覆蓋j列。problem 1 選定最少的行,使得每列有且僅有乙個1.2 選定最少的行,使得每列至少乙個1.dlx原理 這類屬於np問題的問題,可以使用搜尋解決。但是普通的搜尋必超時無疑。因此我們要設法加優化來...

Dancing links 資料結構

前言 想必很多人學習這個演算法,都是因為數獨的原因,雖然這個演算法並不是專門解決數獨的,但是,在解決數獨方面,仍然比暴力搜尋有很大,優勢,在解法方面就顯得很方便,複雜度方面也很客觀,總得來說這個演算法的思想並不是很難理解,但是沒有一種很好的資料結構能很好的實現該演算法,由此dlx這種資料結構就誕生了...

舞蹈鏈(Dancing Links)演算法求解數獨

利用舞蹈鏈 dancing links 演算法求解數獨問題,實際上就是下面乙個流程 1 把數獨問題轉換為精確覆蓋問題 2 設計出資料矩陣 3 用舞蹈鏈 dancing links 演算法求解該精確覆蓋問題 4 把該精確覆蓋問題的解轉換為數獨的解 首先看看數獨問題 9 9的方格 的規則 1 每個格仔只...