关于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();
}
}