跳舞链
跳舞链是著名的计算机科学家高德纳提出的一种使用深度优先搜索来解决精度覆盖问题的算法。其问题的提出是在一个棋盘上,是否可以挑出一些长边,使得每一个宽边上有且仅有一枚棋子。
高德纳认为,这个问题里面宽度和长度是两个相互制约的因素。首先,如果我们选到了一个长边,那么相对应的宽边都得到了覆盖。而与此同时,如果其他长边上的同一位置也存在棋子,那么由于精确性的条件,这些边都不能够再选择了。在搜索的过程中就构成了剪枝的条件。
于是,标准的跳舞链实现就是这样的,使用十字链表存储棋盘上的情况。当横向选取到了一条长边的时候,纵向删除所遇到的所有宽边,在回朔时,重新对其进行恢复结点的操作。这样,我们就可以通过这样的一个数据结构来求解精确覆盖的问题。
位操作实现
我想,在这儿,我们的十字链表实际上相当于一个关系矩阵,如果两个结点直接相连接,那么在一个结点被选中之后,所有的结点都要被删除。那么,我们可以使用位操作方便的实现这些操作。
另外,我们要得到的,是行号的一个组合,所以,我们可以设定以递增或者递减的顺序对链进行遍历。进行剪枝。
代码如下:
#include<iostream>
#include<cstdio>
#include<bitset>
#include<cstring>
#define MAXN 105
using namespace std;
int n,m;
bitset<MAXN>rolcolList[MAXN];
bitset<MAXN>colrolList[MAXN];
bitset<MAXN>colremain;
bitset<MAXN>rolremain;
bool Dfs(int index)
{
//if we have found the answer or there is no rol to choose
//return the result
if(colremain.count() == m)return true;
else if(rolremain.count() == n)return false;
int i,j;
bitset<MAXN>recordrol;
bitset<MAXN>recordcol;
recordcol = colremain;
recordrol = rolremain;
for(i=index;i<=n;i++){
//search for the available row
if(rolremain[i] == 0){
for(j=1;j<=m;j++){
if(rolcolList[i][j] == 1)rolremain |= colrolList[j];
}
colremain |= rolcolList[i];
//we choose the row in an order
if(Dfs(i))return true;
rolremain = recordrol;
colremain = recordcol;
}
}
return false;
}
void Init()
{
rolremain.reset();
colremain.reset();
int i;
for(i=0;i<MAXN;i++){
colrolList[i].reset();
rolcolList[i].reset();
}
}
int main()
{
freopen("input","r",stdin);
int t,i,j,a;
scanf("%d",&t);
while(t--){
Init();
scanf("%d %d",&n,&m);
//input the whole matrix
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
scanf("%d",&a);
if(a){
rolcolList[i][j] = 1;
colrolList[j][i] = 1;
}
}
}
if(Dfs(1))printf("Yes\n");
else printf("No\n");
}
return 0;
}