刚看了DLX,找到hust1017练了一下
为什么叫dancing links呢,据说是因为算法的创造者knuth感觉这个算法太妙了,不尽让人想起美丽的舞蹈,于是就有了这个名字。的确,DLX对搜索的优化让人叹为观止,缩小稀疏图,减小搜索范围,同时能很快的恢复图的构建,非常妙!
momodi 的论文《Dancing Links 在搜索中的应用》 还有原著的英文论文,看看很不错
跳舞链采用的是十字双向循环链表的数据结构,这种结构很容易实现删除和恢复操作。
//实现删除操作
L[R[c]] = L[c];
R[L[c]] = R[c];
//实现恢复操作
U[D[j]] = j;
D[U[j]] = j;
DLX是为了解决精确覆盖问题而设计
精确覆盖问题可以描述为:给定一个01矩阵, 现在要选择一些行,使得每一列有且仅有一个1
删除的过程可以看作是:
找到节点最少的列c,对于所有map[i][c] == 1的行进行删除
对于找到的第i行,对于所有map[i][j] == 1的第j列进行同样的删除操作
1 #include < stdio.h >
2 #include < string .h >
3 #define INF 0x3fffffff
4 #define NN 1004
5 int N, M;
6 int row[NN][NN];
7 int col[NN][NN];
8 int cntr[NN];
9 int cntc[NN];
10 int L[NN * NN], R[NN * NN], U[NN * NN], D[NN * NN], C[NN * NN];
11 int O[NN];
12 int idx, head;
13
14 /* 删除第c列 */
15 void remove( int c){
16 L[R[c]] = L[c];
17 R[L[c]] = R[c];
18
19 int i, j;
20 for (i = D[c]; i != c; i = D[i]){
21 for (j = R[i]; j != i; j = R[j]){
22 U[D[j]] = U[j];
23 D[U[j]] = D[j];
24 cntc[C[j]] -- ;
25 }
26 }
27 }
28 /* 恢复第c列 */
29 void resume( int c){
30 L[R[c]] = c;
31 R[L[c]] = c;
32
33 int i, j;
34 for (i = D[c]; i != c; i = D[i]){
35 for (j = R[i]; j != i; j = R[j]){
36 U[D[j]] = j;
37 D[U[j]] = j;
38 cntc[C[j]] ++ ;
39 }
40 }
41 }
42 int dfs(){
43
44 if (R[head] == head) return 1 ;
45
46 int min = INF;
47 int c, i, j;
48 for (i = R[head]; i != head; i = R[i]){
49 if (cntc[i] < min){
50 c = i;
51 min = cntc[i];
52 }
53 }
54 remove(c);
55 for (i = D[c]; i != c; i = D[i]){
56 O[idx ++ ] = (i - 1 ) / M;
57 for (j = R[i]; j != i; j = R[j]){
58 remove(C[j]);
59 }
60 if (dfs()) return 1 ;
61 /* 这个顺序很重要,删除和恢复的方向必须相反
62 开始相同,都是向右的,结果TLE了 */
63 for (j = L[i]; j != i; j = L[j]){
64 resume(C[j]);
65 }
66 idx -- ;
67 }
68 resume(c);
69 return 0 ;
70 }
71
72 int Build(){
73 int i, j;
74 head = 0 ;
75 for (i = 0 ; i < M; i ++ ){
76 R[i] = i + 1 ;
77 L[i + 1 ] = i;
78 }
79 R[i] = 0 ;
80 L[ 0 ] = i;
81
82 for (i = 1 ; i <= N; i ++ ){
83 for (j = 1 ; j < cntr[i]; j ++ ){
84 R[row[i][j]] = row[i][j + 1 ];
85 L[row[i][j + 1 ]] = row[i][j];
86 }
87 R[row[i][j]] = row[i][ 1 ];
88 L[row[i][ 1 ]] = row[i][j];
89 }
90 for (i = 1 ; i <= M; i ++ ){
91 for (j = 1 ; j < cntc[i]; j ++ ){
92 D[col[i][j]] = col[i][j + 1 ];
93 U[col[i][j + 1 ]] = col[i][j]; // 写成row了
94 }
95 D[col[i][j]] = i;
96 D[i] = col[i][ 1 ];
97 U[i] = col[i][j];
98 U[col[i][ 1 ]] = i;
99 if (cntc[i] == 0 ) return 0 ;
100 }
101 return 1 ;
102 }
103 int main()
104 {
105 int i, j, tmp, a;
106 while (scanf( " %d%d " , & N, & M) != EOF){
107 // memset(cntr, 0, sizeof(cntr));
108 // memset(cntc, 0, sizeof(cntc));
109 for (i = 1 ; i <= M; i ++ ) cntc[i] = 0 ;
110 for (i = 1 ; i <= N; i ++ ){
111 scanf( " %d " , & cntr[i]);
112 for (j = 1 ; j <= cntr[i]; j ++ ){
113 scanf( " %d " , & tmp);
114 row[i][j] = tmp + i * M;
115 cntc[tmp] ++ ;
116 col[tmp][cntc[tmp]] = tmp + i * M;
117 C[tmp + i * M] = tmp;
118 }
119 }
120 if (Build()){
121 idx = 0 ;
122 if (dfs()){
123 printf( " %d " , idx);
124 for (i = 0 ; i < idx; i ++ ) printf( " %d " , O[i]);
125 puts( "" );
126 } else puts( " NO " );
127 } else puts( " NO " );
128 }
129 return 0 ;
130 }
131
傻崽大牛详解:A Crazy Man