dancing links X算法

hust 1017Exact cover

就是knuth论文里拿来举例子的题, 算是精确覆盖的裸题了。


#include <cstdio>
#include <cstring>
/** 在01矩阵找一个行集合,使其精确覆盖所有的列
所谓精确覆盖就是所有行中含1的列有且仅有一个,
而重复覆盖允许有多个。
**/

const int maxn=100000+123;
const int maxc=1000+5;

int S[maxc], L[maxn], R[maxn], D[maxn], U[maxn];
int H[maxc], ok[maxc], sz, C[maxn], mark[maxn];
///H是横向的表头, 不知道必不必要
void Link(int row, int col)
{
    S[col]++; C[sz]=col;///C域指向列头
    U[sz]=U[col]; D[U[col]]=sz;
    D[sz]=col; U[col]=sz;
    
    if(H[row]==-1)H[row]=L[sz]=R[sz]=sz;
    else
    {
        L[sz]=L[H[row]]; R[L[H[row]]]=sz;
        R[sz]=H[row]; L[H[row]]=sz;
    }
    mark[sz]=row;/// 标记每个点是哪一行(题目要求输出解属于哪一行)
    sz++;
}

void remove(int col)
{
    L[R[col]]=L[col];
    R[L[col]]=R[col]; /// 在列对象链表中删除col
    for (int i=D[col]; i!=col; i=D[i])
    {/// 删除col列中有1元素的行
        for (int j=R[i]; j!=i; j=R[j])
        {///删除每行的1元素,并修改所在列的S域
            U[D[j]]=U[j], D[U[j]]=D[j];
            S[C[j]]--;
        }
    }
}

void resume(int col)
{
    for (int i=U[col]; i!=col; i=U[i])
    {
        for (int j=L[i]; j!=i; j=L[j])
        {
            U[D[j]]=j; D[U[j]]=j;
            S[C[j]]++;
        }///恢复删除的元素,恢复S域
    }///恢复删除的行
    L[R[col]]=col;
    R[L[col]]=col;
}

bool Dance(int k)
{
    //printf("%d\n", k);
    if(R[0]==0)
    {
        printf("%d", k);
        for (int i=0; i<k; ++i)
            printf(" %d", ok[i]);
        puts("");
        return true;
    }
    int c=R[0];
    for(int i=R[0]; i; i=R[i])
        if(S[i]<S[c])c=i;
    remove(c);
//    printf("remove col %d\n", c);
    for(int i=D[c]; i!=c; i=D[i])
    {
        ok[k]=mark[i];
        for (int j=R[i]; j!=i; j=R[j])
            remove(C[j]);
        if(Dance(k+1))return true;
        for (int j=L[i]; j!=i; j=L[j])
            resume(C[j]);
    }
    resume(c);
    return false;
}

void initL(int x)
{
    for (int i=0; i<=x; ++i)///i<x???
    {
        S[i]=0;
        D[i]=U[i]=i;
        L[i+1]=i; R[i]=i+1;
    }///对列表头初始化
    R[x]=0;
    sz=x+1;///真正的元素从m+1开始
    memset (H, -1, sizeof(H));
    ///mark每个位置的名字
}

int main()
{
    int n, m; 
    while(~scanf("%d%d", &n, &m))
    {
        initL(m);
        for (int i=1; i<=n; ++i)
        {
            int k; scanf("%d", &k);
            while (k--)
            {
                int x; scanf("%d", &x);
                Link(i, x);
            }
        }
        if(!Dance(0))puts("NO");
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值