精确覆盖一类的问题,难点在于01矩阵的构造。一旦构造好,就可以用下面的dancing links 技术快速有效地求解。
构造思路:
每个物体所有可能的摆放方式 --行
不重复地覆盖所有格子 -- 目标列
约束条件,或者单个选择的唯一表示 -- 列
把可能的选择,包括已知的选择 --行
比如对于数独问题[2]:
(1)81个格子中每个格子只能放一个数字C1-81
(2)每一行的数字不能重复C82-162
(3)每一列的数字不能重复C163-243
(4)每一九宫内的数字不能重复 C244-324
9,2,0,0,0,0,0,0,0:
9 -- 在1到81列的第一列,在82到162列的第9个,即90列,在163列到243列的第9个,在244到324列的第9个各占一个1
2 -- ...
0 -- 9种选择
以下代码完全参考[1]的做的,打印所有可能的解。当做模板,供以后查询:
/*精确覆盖问题: 对于一个01矩阵,选择若干行,使得所有1的位置都被覆盖且只被覆盖一次。 X算法: 试探性的选择某一行作为解, 该行覆盖的所有列不用再考虑了,删除; 与该行冲突的哪些行不能选了,删除。 这样原问题的规模缩小,继续在子问题上重复上述过程。 结束条件是目标:所有列都覆盖了。 这里的删除只是临时的,如果选择的行导致最终并没有完全覆盖所有列, 还需要恢复删除的行和列。 这是回溯技术--在搜索树上的深度优先遍历,删除对应标记某些节点不用考虑了。 choose a column c (determin). choose a row r such that A[r,c] = 1 (non-determin). include r in the partial solution. for each j such that A[r,j] = 1, delete column j; for each i such that A[i,j] = 1, delete row i. recusively work on the reduced matrix. 例如,对于矩阵, C1 C2 C3 C4 C5 C6 C7 0 0 1 0 1 1 0 1 0 0 1 0 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 0 1 从第一列开始,选择 r=2, j = 4,7, 删除 C4, R2, R4, R6, C7,R2,R5,R6. 剩下C2,C3,C5,C6; R1,R3. 选择C2,只有r=3, j=2,3,6, 删除 C2,R3, C3,R1,R3,C6,R1,R3. 选择C5, 没有行可选来覆盖C5, 回溯到r=2这层,选择r=3,... 只把矩阵中的1连接起来,构成交叉十字循环双向链, 每个元素包含指向左右上下列行的指针。 data object: left, right -- link elements of the same row up, down -- link elements of the same col column -- link to the column header column header object: left, right -- link all columns not covered up, down -- link to first/last elements ot this column size -- number of 1s in the column name -- name of the column h -- column header root, only left,right are used. 代码实现: 行没有连表头,处理与列有些不同。 o 构造时; o 覆盖时, c C[j] | D | r --R-- j 两个地方的cover_column都是针对行r的。 再看看print_solution就知道了,这是 行还没有连表头才这样子写的。 */ #include <stdio.h> #define ROW 256 #define COL 256 #define NUM (1+COL+ROW*COL) static int L[NUM], R[NUM], U[NUM], D[NUM], C[NUM]; static int Y[NUM]; //row link static int h; static int S[1+COL]; //number of 1s in the column static char N[1+COL]; //name of columns static int row, col; //actual row and col number static int O[ROW]; //choosed rows, partial solution void cover_column(int c) { int i, j; L[R[c]] = L[c]; R[L[c]] = R[c];//delete column c for(i = D[c]; i != c; i = D[i]){ for(j = R[i]; j != i; j = R[j]){//delete row i U[D[j]] = U[j]; D[U[j]] = D[j]; S[C[j]]--; } } } void uncover_column(int c) { int i, j; for(i = U[c]; i != c; i = U[i]){ for(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; } void print_solution(int k) { int i, j, r; for(i = 0; i < k; ++i){ r = O[i]; printf("row %d: col %c ", Y[r], N[C[r]]); for(j = R[r]; j != r; j = R[j]){//? printf("%c ", N[C[j]]); } printf("\n"); } printf("===using %d rows\n", k); } int choose_column(void) { int c, s, j; c = R[h]; s = S[c];//select smallest branching factor for(j = R[c]; j != h; j = R[j]){ if(S[j] < s){ c = j; s = S[j]; } } return c; } void search(int k) { int c, r, j; if(R[h] == h){ print_solution(k); return; } c = choose_column(); cover_column(c); for(r = D[c]; r != c; r = D[r]){ O[k] = r; for(j = R[r]; j != r; j = R[j]){ cover_column(C[j]); } search(k+1); for(j = L[r]; j != r; j = L[j]){ uncover_column(C[j]); } } uncover_column(c); } void insert_column(int x, int c) { C[x] = c; D[x] = c; U[x] = U[c]; U[D[x]] = x; D[U[x]] = x; S[c]++; } void insert_row(int x, int r) { R[x] = r; L[x] = L[r]; L[R[x]] = x; R[L[x]] = x; } int main() { int i, j, val, loc, rlo; scanf("%d%d", &row, &col); h = 0; L[h] = col; R[h] = 1; for(i = 1; i <= col; ++i){ L[i] = i-1; R[i] = i < col ? i+1 : h; U[i] = D[i] = i; C[i] = 0; S[i] = 0; N[i] = 'A' + i-1; } loc = col; for(j = 0; j < row; ++j){ rlo = 0; for(i = 0; i < col; ++i){ scanf("%d", &val); if(val){ ++loc; insert_column(loc, 1+i); Y[loc] = j+1; if(!rlo){ rlo = loc; L[loc] = R[loc] = loc; }else{ insert_row(loc, rlo); } } } } search(0); return 0; }
[1] dancing links, http://arxiv.org/pdf/cs/0011047.pdf
[2] http://blog.csdn.net/mu399/article/details/7627862