Dancing Links (DLX)是Knuth爲了解決精確覆蓋問題而提出的算法,不少搜索問題能夠轉化位精確覆蓋問題從而使用Dancing Links解決(效率會比DFS高不少,由於裏面經常蘊涵着意想不到的剪枝)php
信息學競賽中的DLX的問題相似網絡流,只需建圖+貼版便可html
參考文獻:node
一、DLX的原理:Knuth的論文:算法
原版:http://arxiv.org/abs/cs/0011047網絡
翻譯版:http://wenku.baidu.com/view/d8f13dc45fbfc77da269b126.htmlide
二、DLX在搜索中的應用(DLX應用於信息學競賽的論文):http://bbs.whu.edu.cn/wForum/elite.php?file=%2Fgroups%2FGROUP_3%2FACM_ICPC%2Fnx08%2FD.08010000%2FM.1215524645.R0&ap=563函數
首先介紹精確覆蓋問題概念:spa
精確覆蓋:在一個全集$X$中若干子集的集合爲$S$,精確覆蓋是指,$S$的子集$S*$,知足$X$中的每個元素在$S*$中\textbf{剛好}出現一次。翻譯
即:給你一個0-1矩陣,選取若干行,使得選取的行組成的新矩陣的每一列有且僅有一個13d
關於DLX的原理這裏就不詳細介紹了,有興趣瞭解者能夠去閱讀Knuth的論文
DLX精確覆蓋模板(From kuangbin):
1 const int maxnode = 100010; 2 const int maxr = 1010; 3 const int maxc = 1010; 4 struct DLX 5 { 6 int n, m, size; 7 int U[maxnode], D[maxnode], R[maxnode], L[maxnode], Row[maxnode], Col[maxnode]; 8 int H[maxc], S[maxr]; 9 int ansd, ans[maxc]; 10 void init(int _n, int _m) 11 { 12 n = _n; 13 m = _m; 14 for (int i = 0; i <= m; i++) 15 { 16 S[i] = 0; 17 U[i] = D[i] = i; 18 L[i] = i - 1; 19 R[i] = i + 1; 20 } 21 R[m] = 0; L[0] = m; 22 size = m; 23 for (int i = 1; i <= n; i++) 24 H[i] = -1; 25 } 26 void Link(int r, int c) 27 { 28 ++S[Col[++size] = c]; 29 Row[size] = r; 30 D[size] = D[c]; 31 U[D[c]] = size; 32 U[size] = c; 33 D[c] = size; 34 if (H[r] < 0)H[r] = L[size] = R[size] = size; 35 else 36 { 37 R[size] = R[H[r]]; 38 L[R[H[r]]] = size; 39 L[size] = H[r]; 40 R[H[r]] = size; 41 } 42 } 43 void remove(int c) 44 { 45 L[R[c]] = L[c]; R[L[c]] = R[c]; 46 for (int i = D[c]; i != c; i = D[i]) 47 for (int j = R[i]; j != i; j = R[j]) 48 { 49 U[D[j]] = U[j]; 50 D[U[j]] = D[j]; 51 --S[Col[j]]; 52 } 53 } 54 void resume(int c) 55 { 56 for (int i = U[c]; i != c; i = U[i]) 57 for (int j = L[i]; j != i; j = L[j]) 58 ++S[Col[U[D[j]] = D[U[j]] = j]]; 59 L[R[c]] = R[L[c]] = c; 60 } 61 //d爲遞歸深度 62 bool Dance(int d) 63 { 64 if (R[0] == 0) 65 { 66 ansd = d; 67 return true; 68 } 69 int c = R[0]; 70 for (int i = R[0]; i != 0; i = R[i]) 71 if (S[i] < S[c]) 72 c = i; 73 remove(c); 74 for (int i = D[c]; i != c; i = D[i]) 75 { 76 ans[d] = Row[i]; 77 for (int j = R[i]; j != i; j = R[j])remove(Col[j]); 78 if (Dance(d + 1))return true; 79 for (int j = L[i]; j != i; j = L[j])resume(Col[j]); 80 } 81 resume(c); 82 return false; 83 } 84 };
PS:init函數初始化,0-1矩陣中爲1的位置(i,j)用Link(i,j)添加約束,Dance函數爲解決過程(bool表示可否完成),ansd爲選取的數量,and[]爲選取的行
精確覆蓋問題能夠延伸爲重複覆蓋問題,定義以下:
重複覆蓋:在一個全集$X$中若干子集的集合爲$S$,重複覆蓋是指,$S$的子集$S*$,知足$X$中的每個元素在$S*$中\textbf{至少}出現一次。
即:給你一個0-1矩陣,選取若干行,使得選取的行組成的新矩陣的每一列至少有一個1
DLX重複覆蓋模板
1 const int maxnode = 360000; 2 const int maxc = 500; 3 const int maxr = 500; 4 const int inf = 0x3f3f3f3f; 5 struct DLX 6 { 7 int L[maxnode], R[maxnode], D[maxnode], U[maxnode], C[maxnode]; 8 int S[maxc], H[maxr], size; 9 int ans; 10 ///不須要S域 11 void Link(int r, int c) 12 { 13 S[c]++; C[size] = c; 14 U[size] = U[c]; D[U[c]] = size; 15 D[size] = c; U[c] = size; 16 if (H[r] == -1) H[r] = L[size] = R[size] = size; 17 else 18 { 19 L[size] = L[H[r]]; R[L[H[r]]] = size; 20 R[size] = H[r]; L[H[r]] = size; 21 } 22 size++; 23 } 24 void remove(int c) 25 { 26 for (int i = D[c]; i != c; i = D[i]) 27 L[R[i]] = L[i], R[L[i]] = R[i]; 28 } 29 void resume(int c) 30 { 31 for (int i = U[c]; i != c; i = U[i]) 32 L[R[i]] = R[L[i]] = i; 33 } 34 int h() ///用精確覆蓋去估算剪枝 35 { 36 int ret = 0; 37 bool vis[maxc]; 38 memset (vis, false, sizeof(vis)); 39 for (int i = R[0]; i; i = R[i]) 40 { 41 if (vis[i])continue; 42 ret++; 43 vis[i] = true; 44 for (int j = D[i]; j != i; j = D[j]) 45 for (int k = R[j]; k != j; k = R[k]) 46 vis[C[k]] = true; 47 } 48 return ret; 49 } 50 //根據具體問題選擇限制搜索深度或直接求解。 51 bool Dance(int k) 52 { 53 if (k + h() >= ans) return 0; 54 if (!R[0]) 55 { 56 if (k < ans)ans = k; 57 return 1; 58 } 59 int c = R[0]; 60 for (int i = R[0]; i; i = R[i]) 61 if (S[i] < S[c])c = i; 62 for (int i = D[c]; i != c; i = D[i]) 63 { 64 remove(i); 65 for (int j = R[i]; j != i; j = R[j]) 66 remove(j); 67 Dance(k + 1); 68 for (int j = L[i]; j != i; j = L[j]) 69 resume(j); 70 resume(i); 71 } 72 return 0; 73 } 74 void initL(int x) ///col is 1~x,row start from 1 75 { 76 for (int i = 0; i <= x; ++i) 77 { 78 S[i] = 0; 79 D[i] = U[i] = i; 80 L[i + 1] = i; R[i] = i + 1; 81 }///對列表頭初始化 82 R[x] = 0; 83 size = x + 1; ///真正的元素從m+1開始 84 memset (H, -1, sizeof(H)); 85 ///mark每一個位置的名字 86 } 87 };
輸入:Link()
輸出:ans, bool Dance(int k)
重點來了:如何建圖?這裏選取典型例題說明
(由於以前丟了一次稿,未完待續)