HUST 1017 Exact cover 跳舞链(DLX) 模板题目

http://acm.hust.edu.cn:8080/judge/problem/viewProblem.action?id=10702

题意:

给你一个n*m的01矩阵,让你选择若干行使这些行中的1能够覆盖所有的列,而且不能出现重复覆盖。输出所选的行。

思路:

这是跳舞链的模板题目。不多说了。

给出个人觉得讲解比较好的链接:http://blog.csdn.net/mu399/article/details/7627736

参考代码:http://blog.csdn.net/dooder_daodao/article/details/6654904

 

我的代码:

View Code
#include <cstdio>
#include <cstring>
#include <iostream>


using namespace std;

#define CL(a,num) memset((a),(num),sizeof(a))
#define inf 0x7f7f7f7f
#define M 1007
#define N 1000007

const int head = 0;
int u[N],d[N],l[N],r[N],c[N],row[N];
int s[M],o[M];
int ak,n,m;

void init(int m){
    int i;
    for (i = 1; i <= m; ++i){
        l[i] = i - 1;
        r[i] = i + 1;
        u[i] = d[i] = i;
        c[i] = i;
        s[i] = 0;
    }
    l[head] = m; r[head] = 1;
    r[m] = head;
}

void remove(int ci){
    int i,j;
    l[r[ci]] = l[ci];
    r[l[ci]] = r[ci];

    for (i = d[ci]; i != ci; i = d[i]){
        for (j = r[i]; j != i; j = r[j]){
            u[d[j]] = u[j];
            d[u[j]] = d[j];
            s[c[j]]--;
        }
    }
}
void resume(int ci){
    int i,j;
    l[r[ci]] = r[l[ci]] = ci;
    for (i = u[ci]; i != ci; i = u[i]){
        for (j = l[i]; j != i; j = l[j]){
            u[d[j]] = d[u[j]] = j;
            s[c[j]]++;
        }
    }
}
int dfs(int k){
    int i,j;
    //若列对象为空,说明所有列已经覆盖,返回值
    if (r[head] == head){
        ak = k;
        return 1;
    }
    //每次着该列里面1最少的
    int MIN = inf, ci = 0;
    for (i = r[head]; i != head; i = r[i]){
        if (s[i] < MIN){
            MIN = s[i];
            ci = i;
        }
    }
    remove(ci);//删除该列对象以及该列所覆盖的行
    for (i = d[ci]; i != ci; i = d[i]){
        for (j = r[i]; j != i; j = r[j]){
            remove(c[j]);//选择i作为覆盖c列的行,并且要删调该行所覆盖的列
        }
        o[k] = row[i];//记录结果
        if (dfs(k + 1)) return 1;//继续选择列

    //i列不能满足还原i列
        for (j = l[i]; j != i; j = l[j]){
            resume(c[j]);
        }
    }
    resume(ci);
    return 0;
}
int main(){
    int i,j;
    int num,size;
    while (~scanf("%d%d",&n,&m)){
      //更新列对象
       init(m);
        size = m + 1;//记录第几个
        int x;
        for (i = 0; i < n; ++i){
            scanf("%d",&num);
            int rh = -1;
            for (j = 0; j < num; ++j){
                scanf("%d",&x);

                s[x]++;//记录x列有多少个1
                c[size] = x;//记录第size个的列
                row[size] = i + 1;//记录第size个的行
        //插入列,挂链
                u[size] = u[x];
                d[u[x]] = size;
                u[x] = size;
                d[size] = x;

        //插入行,挂链
                if (rh == -1){
                    l[size] = r[size] = size;
                    rh = size;
                }
                else{
                    l[size] = l[rh];
                    r[l[rh]] = size;
                    l[rh] = size;
                    r[size] = rh;
                }
                size++;
            }
        }
        if (dfs(0)){
            printf("%d",ak);
            for (i = 0; i < ak; ++i) printf(" %d",o[i]);
            printf("\n");
        }
        else{
            printf("NO\n");
        }
    }
    return 0;
}

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值