精确覆盖问题的dancing links 技术

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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值