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 每個格仔只...