精确覆盖一类的问题,难点在于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
#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