用位操作实现跳舞链

跳舞链

跳舞链是著名的计算机科学家高德纳提出的一种使用深度优先搜索来解决精度覆盖问题的算法。其问题的提出是在一个棋盘上,是否可以挑出一些长边,使得每一个宽边上有且仅有一枚棋子。
高德纳认为,这个问题里面宽度和长度是两个相互制约的因素。首先,如果我们选到了一个长边,那么相对应的宽边都得到了覆盖。而与此同时,如果其他长边上的同一位置也存在棋子,那么由于精确性的条件,这些边都不能够再选择了。在搜索的过程中就构成了剪枝的条件。
于是,标准的跳舞链实现就是这样的,使用十字链表存储棋盘上的情况。当横向选取到了一条长边的时候,纵向删除所遇到的所有宽边,在回朔时,重新对其进行恢复结点的操作。这样,我们就可以通过这样的一个数据结构来求解精确覆盖的问题。

位操作实现

我想,在这儿,我们的十字链表实际上相当于一个关系矩阵,如果两个结点直接相连接,那么在一个结点被选中之后,所有的结点都要被删除。那么,我们可以使用位操作方便的实现这些操作。
另外,我们要得到的,是行号的一个组合,所以,我们可以设定以递增或者递减的顺序对链进行遍历。进行剪枝。
代码如下:

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值