關於Dancing Links的介紹最好仔細閱讀一下Knuth的論文,那篇翻譯的文章也不錯,這位仁兄的 部落格比較易懂http://blog.csdn.net/sunny606/article/details/7833551
通過閱讀論文主要有如下幾點體會:
1.精確覆蓋的最基本模型是:在一個01矩陣中選擇若干個行,使得沒列都包含且僅包含一個1
2.Dangcing links的邏輯與普通dfs暴力搜尋相似,但它利用四向迴圈鏈表進行了犀利的剪枝,使得當搜尋進入下一層時鏈表變得很稀疏,且結合了一些啟發函數,因此雖然複雜度理論上是指數級的,但實際速度很快,可以在一秒內處理1000*1000規模的矩陣。
3.四向迴圈鏈表將矩陣中所有1串連,當選取了某一行時,將與之矛盾的行列暫時從鏈表中刪除進入下一層搜尋,由於只是修改指標不是真正刪除,因此在搜尋失敗後能快速恢複鏈表選取下一方案。
4.關於建模:沒列代表一個限制條件,每行代表一種方案,若方案i滿足限制j,則矩陣mat[i][j]=1,因此矩陣的構造既要滿足所有限制條件,又不能漏掉可能的方案。
以解決數獨問題為例:首先看限制:每一行每一列每一個小塊中1--9都只能出現一次,且每個格子都要選取一個元素,因此有9*(9+9+9)+81個限制。
限制:每個格子可以去所有與已有元素不矛盾的元素,每種方案都滿足四種限制。
題目:poj3074 poj 2676 poj3076 hdu 4069 hdu3909
hdu3909比較綜合,數獨的階數不確定,要判斷無解、多解、給出的矩陣是否是最小矩陣(若去掉任意一個元素後都使得矩陣有多解則稱為最小矩陣)
多解只需在dance過程中修改一下返回條件即可,找到第二個解後再返回,判斷最小矩陣只需逐個去掉矩陣中的元素判多解即可。
import java.util.Arrays;import java.util.Scanner;public class Main{ class DLX { int maxn = 20010; int L[] = new int[maxn], R[] = new int[maxn], D[] = new int[maxn], U[] = new int[maxn]; int Row[] = new int[maxn], C[] = new int[maxn], S[] = new int[maxn]; // 元素x所在行列 每列元素個數 int m, id, rowid; int ans[] = new int[maxn], cnt; void init(int m) { this.m = m; mul = 0; for (int i = 0; i <= m; i++) { D[i] = U[i] = i; S[i] = 0; L[i] = i - 1; R[i] = i + 1; } L[0] = m; R[m] = 0; id = m + 1; cnt = rowid = 0; mul = 0; } void insert(int arr[], int len) { for (int i = 0; i < len; i++, id++) { int x = arr[i]; C[id] = x; Row[id] = rowid; S[x]++; D[id] = x; U[id] = U[x]; D[U[x]] = id; U[x] = id; if (i == 0) L[id] = R[id] = id; else { L[id] = id - 1; R[id] = id - i; L[id - i] = id; R[id - 1] = id; } } rowid++; } void remove(int c) { L[R[c]] = L[c]; R[L[c]] = R[c]; for (int i = D[c]; i != c; i = D[i]) for (int j = R[i]; j != i; j = R[j]) { S[C[j]]--; U[D[j]] = U[j]; D[U[j]] = D[j]; } } void resume(int c) { for (int i = U[c]; i != c; i = U[i]) for (int j = L[i]; j != i; j = L[j]) { S[C[j]]++; U[D[j]] = j; D[U[j]] = j; } L[R[c]] = c; R[L[c]] = c; } int mul = 0; boolean dance() { if (R[0] == 0) { if (++mul > 1) return true; for (int i = 0; i < cnt; i++) res[p[ans[i]].i][p[ans[i]].j] = p[ans[i]].val; return false; } int c = R[0]; for (int i = R[0]; i != 0; i = R[i]) if (S[i] < S[c]) c = i; remove(c); for (int i = D[c]; i != c; i = D[i]) { ans[cnt++] = Row[i]; for (int j = R[i]; j != i; j = R[j]) remove(C[j]); if (dance()) return true; for (int j = L[i]; j != i; j = L[j]) resume(C[j]); cnt--; } resume(c); return false; } } class plan { int i, j, val; plan(int i, int j, int v) { this.j = j; this.i = i; val = v; } } int map[][] = new int[20][20], res[][] = new int[20][20], m, hash[] = new int[255]; char rehash[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G' }; boolean flag[] = new boolean[20]; plan p[] = new plan[5010]; DLX dlx = new DLX(); Scanner scan = new Scanner(System.in); void build(int i, int j) { if (map[i][j] != 0) { Arrays.fill(flag, false); flag[map[i][j]] = true; return; } Arrays.fill(flag, true); for (int k = 1; k <= m * m; k++) flag[map[k][j]] = flag[map[i][k]] = false; i = (i - 1) / m * m + 1; j = (j - 1) / m * m + 1; for (int x = 0; x <m; x++) for (int y = 0; y <m; y++) flag[map[i + x][j + y]] = false; } int grid(int i, int j) { return (i - 1) / m * m + (j - 1) / m + 1; } void init() { int cnt = 0; for (int i = '0'; i <= '9'; i++) hash[i] = cnt++; for (int i = 'A'; i <= 'G'; i++) hash[i] = cnt++; } void work() { int M = m * m; dlx.init(M * M * 4); int arr[] = new int[4]; int idx = 0; for (int i = 1; i <= M; i++) for (int j = 1; j <= M; j++) { build(i, j); for (int k = 1; k <= M; k++) if (flag[k]) { arr[0] = (i - 1) * M + k;// 行 arr[1] = M * M + (j - 1) * M + k;// 列 arr[2] = M * M * 2 + (grid(i, j) - 1) * M + k;// 塊 arr[3] = M * M * 3 + (i - 1) * M + j;// 格子 dlx.insert(arr, 4); p[idx++] = new plan(i, j, k); } } dlx.dance(); } void run() { init(); while (scan.hasNext()) { m = scan.nextInt(); for (int i = 1; i <= m * m; i++) { String s = scan.next(); for (int j = 1; j <= m * m; j++) map[i][j] = hash[s.charAt(j - 1)]; } work(); if (dlx.mul == 2) System.out.println("Multiple Solutions"); if (dlx.mul == 0) System.out.println("No Solution"); if (dlx.mul == 1) if (minimal()) { work(); print(); } else System.out.println("Not Minimal"); } } boolean minimal() { for (int i = 1; i <= m * m; i++) for (int j = 1; j <= m * m; j++) if (map[i][j] != 0) { int temp = map[i][j]; map[i][j] = 0; work(); map[i][j] = temp; if (dlx.mul == 1){ return false; } } return true; } void print() { for (int i = 1; i <= m * m; i++) { for (int j = 1; j <= m * m; j++) System.out.print(rehash[res[i][j]]); System.out.println(); } } public static void main(String[] args) { new Main().run(); }}