本文内容遵从CC版权协议 转载请注明出自: http://blog.csdn.net/masterluo
Dancing Links是解决完美覆盖问题的算法,我不得不说,这是我所见过的最优美的算法。难怪
也许你己经看出来了这是9*9数独的强化版。你也许玩过下面这个游戏:
将这样一类N2 *N2 的数独问题进行转化的思想是一样的。对于这样一类问题都可以转化成N6 行4 * N4 列的01矩阵,而且易知,其实对于每个位置的每个数来说,它只包含4个1,分别是位置,行的位置,列的位置,块的位置,其余位置都是0。这样对于这类关系就有很清楚的认识了。你只需要对于每个位置的每个数,对它进行变换为在原01矩阵中的位置再加入双向十字链表即可。
下面给出01矩阵的完美覆盖算法的代码:
-
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
/***最大行***/
#define MAXROW 16
/***最大列***/
#define MAXCOL 300
struct DancingLinksNode {
/***结点所在的行列位置***/
int r , c;
/***结点的上下左右结点指针***/
DancingLinksNode * U , * D , * L , * R;
};
/****备用结点****/
DancingLinksNode node [ MAXROW * MAXCOL ];
/****行头****/
DancingLinksNode row [ MAXROW ];
/****列头****/
DancingLinksNode col [ MAXCOL ];
/****表头****/
DancingLinksNode head;
/****使用了多少结点****/
int cnt;
/****列含有多少个域****/
int size [ MAXCOL ];
/****表的行与列变量****/
int m , n;
/****初始化,r, c分别表示表的大小***/
void init( int r , int c) {
/****将可以使用的结点设为第一个****/
cnt = 0;
/****head结点的r,c分别表示表的大小,以备查****/
head . r = r;
head . c = c;
/****初始化head结点****/
head . L = head . R = head . U = head . D = & head;
/***初始化列头***/
for( int i = 0; i < c; ++ i) {
col [ i ]. r = r;
col [ i ]. c = i;
col [ i ]. L = & head;
col [ i ]. R = head . R;
col [ i ]. L -> R = col [ i ]. R -> L = & col [ i ];
col [ i ]. U = col [ i ]. D = & col [ i ];
size [ i ] = 0;
}
/***初始化行头,在删除的时候,如果碰到row[i].c == c的情形应当被跳过***/
for( int i = r - 1; i > - 1; -- i) {
row [ i ]. r = i;
row [ i ]. c = c;
row [ i ]. U = & head;
row [ i ]. D = head . D;
row [ i ]. U -> D = row [ i ]. D -> U = & row [ i ];
row [ i ]. L = row [ i ]. R = & row [ i ];
}
}
/****增加一个结点,在原表中的位置为r行,c列***/
inline void addNode( int r , int c) {
/****找一个未曾使用的结点****/
DancingLinksNode * ptr = & node [ cnt ++ ];
/****设置结点的行列号****/
ptr -> r = r;
ptr -> c = c;
/****将结点加入双向链表中****/
ptr -> R = & row [ r ];
ptr -> L = row [ r ]. L;
ptr -> L -> R = ptr -> R -> L = ptr;
ptr -> U = & col [ c ];
ptr -> D = col [ c ]. D;
ptr -> U -> D = ptr -> D -> U = ptr;
/****将size域加1****/
++ size [ c ];
}
/****删除ptr所指向的结点的左右方向****/
inline void delLR( DancingLinksNode * ptr) {
ptr -> L -> R = ptr -> R;
ptr -> R -> L = ptr -> L;
}
/****删除ptr所指向的结点的上下方向****/
inline void delUD( DancingLinksNode * ptr) {
ptr -> U -> D = ptr -> D;
ptr -> D -> U = ptr -> U;
}
/****重置ptr所指向的结点的左右方向****/
inline void resumeLR( DancingLinksNode * ptr) {
ptr -> L -> R = ptr -> R -> L = ptr;
}
/****重置ptr所指向的结点的上下方向****/
inline void resumeUD( DancingLinksNode * ptr) {
ptr -> U -> D = ptr -> D -> U = ptr;
}
/****覆盖第c例***/
inline void cover( int c) {
/**** c == n 表示头****/
if( c == n) {
return;
}
/****删除表头****/
delLR( & col [ c ]);
DancingLinksNode * R , * C;
for( C = col [ c ]. D; C != ( & col [ c ]); C = C -> D) {
if( C -> c == n)
continue;
for( R = C -> L; R != C; R = R -> L ){
if( R -> c == n)
continue;
-- size [ R -> c ];
delUD( R);
}
delLR( C);
}
}
/****重置第c列****/
inline void resume( int c) {
if( c == n)
return;
DancingLinksNode * R , * C;
for( C = col [ c ]. U; C != ( & col [ c ]); C = C -> U) {
if( C -> c == n)
continue;
resumeLR( C);
for( R = C -> R; R != C; R = R -> R) {
if( R -> c == n)
continue;
++ size [ R -> c ];
resumeUD( R);
}
}
/****把列头接进表头中****/
resumeLR( & col [ c ]);
}
/****搜索核心算法,k表示搜索层数****/
bool search( int k) {
/***搜索成功,返回true***/
if( head . L == ( & head)) {
return true;
}
/***c表示下一个列对象位置,找一个分支数目最小的进行覆盖***/
int INF = ( 1 << 30 ), c = - 1;
for( DancingLinksNode * ptr = head . L; ptr != ( & head); ptr = ptr -> L) {
if( size [ ptr -> c ] < INF) {
INF = size [ ptr -> c ];
c = ptr -> c;
}
}
/***覆盖第c列***/
cover( c);
DancingLinksNode * ptr;
for( ptr = col [ c ]. D; ptr != ( & col [ c ]); ptr = ptr -> D) {
DancingLinksNode * rc;
ptr -> R -> L = ptr;
for( rc = ptr -> L; rc != ptr; rc = rc -> L) {
cover( rc -> c);
}
ptr -> R -> L = ptr -> L;
if( search( k + 1)) {
return true;
}
ptr -> L -> R = ptr;
for( rc = ptr -> R; rc != ptr; rc = rc -> R) {
resume( rc -> c);
}
ptr -> L -> R = ptr -> R;
}
/***取消覆盖第c列***/
resume( c);
return false;
}
int main() {
printf( "输入矩阵的行与列(输入EOF结束):");
while( scanf( "%d %d" , & m , &n) != EOF) {
int tvb;
init( m , n);
printf( "请输入01矩阵(以行优先): /n ");
for( int i = 0; i < m; ++ i) {
for( int j = 0; j < n; ++ j) {
scanf( "%d" , & tvb);
if( tvb) {
addNode( i , j);
}
}
}
if( search( 0)) {
puts( "存在完美覆盖");
} else {
puts( "不存在完美覆盖");
}
}
return 0;
}