Dancing Links 小結 (由於以前丟了一次稿,未完待續)

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 };
View Code

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 };
View Code

輸入:Link()
輸出:ans, bool Dance(int k)

 

重點來了:如何建圖?這裏選取典型例題說明

 

(由於以前丟了一次稿,未完待續)

相關文章
相關標籤/搜索