舞蹈链

#include<bits/stdc++.h>
using namespace std;
const int maxnode = 100010;
const int maxm = 1010;
const int maxn = 1010;
struct DLX{
    int n,m,sizes;//行数,列数,‘1’的总数
    int D[maxnode],U[maxnode],L[maxnode],R[maxnode];//U D R L用来记录某个标号的节点的上下左右节点的编号
    int Row[maxnode],Col[maxnode];//Row Col用来记录某个标号的节点在矩阵中的行号和列号
    int H[maxn],S[maxm];//H是行头,S保存某一列中1的数量
    int ansd,ans[maxn];//ansd是答案行数,ans[maxn]是每行行号
    void init(int _n,int _m){//初始化列头
        n=_n,m=_m;//读出行列数
        for(int i=0; i<=m; i++){
            S[i]=0;//每列‘1’的数量为0
            D[i]=U[i]=i;//i号结点的上下节点编号是i即本身结点编号,因为只有其一个
            L[i]=i-1,R[i]=i+1;//i号结点的左右节点编号是i-1,i+1
        }
        R[m]=0,L[0]=m;//0号点与m号点循环
        sizes=m;//至此前m(还有0号点)个点初始化完成
        for(int i=1; i<=n; i++) H[i]=-1;
    }
    void Link(int r,int c){//连边!第R行,第C列写1
        ++S[Col[++sizes]=c];//总'1'量SIZES加1个,然后第SIZES编号的节点的列是c,该列‘1’数量即S数组+1
        Row[sizes]=r;//此节点行号是r
        U[sizes]=U[c];//本结点的上指针指向表头指的上指针,实即上次本列的最后一个1编号
        D[U[c]]=sizes;//上次本列的最后一个编号的下指针指向本结点
        D[sizes]=c;//本结点的下指针指向表头
        U[c]=sizes;//本列表头上指针指向本结点
        if(H[r]<0)//如果此行未有点,即head指针仍为-1,则此点的head指针及左右指针均指向这个点
            H[r]=L[sizes]=R[sizes]=sizes;
        else{//如果此行已有点
            R[sizes]=R[H[r]];//此点的右指针指向本行头指针的右结点
            L[R[H[r]]]=sizes;//上次本行的最右点的左指针指向本结点(不是该右吗)
            L[sizes]=H[r];//本结点左指针为本行头指针,注意h[r]从未变过,就是本行第一个‘1’的位置
            R[H[r]]=sizes;//行首右指针改为本结点
        }
    }
    void remove(int c){//删除某列!并删除列中为1的行
        L[R[c]]=L[c];R[L[c]]=R[c];//右点左针指左点,左点右针指右点,注意控制的只是表头!!!
        for(int i=U[c]; i!=c; i=U[i])//往下逐行扫,循环回到本行
            for(int j=R[i]; j!=i; j=R[j]){//往右逐列扫,循环回到本列
                D[U[j]]=D[j];//当前j结点上面的结点的下面的结点记为j结点上面的结点,实现删除本结点
                U[D[j]]=U[j];//当前j结点下面的结点的上面的结点记为j结点下面的结点,实现删除本结点
                --S[Col[j]];//本结点所在行的‘1’数量减一
            }
    }
    void resume(int c){//反着恢复某列!并恢复列中为1的行
        for(int i=D[c]; i!=c; i=D[i])//往下走
            for(int j=L[i]; j!=i; j=L[j]){//往左走
                D[U[j]]=U[D[j]]=j;//上面的下针,下面的上针指向本结点
                ++S[Col[j]];//计数加1
            }
        L[R[c]]=R[L[c]]=c;//右面的左针,左面的右针指本列,注意这是表头!!!
    }
    bool dance(int d){//舞蹈一下,可得答案!d为递归深度
        if(R[0]==0){ansd=d;return true;}//行表头循环搜回了自身,结束
        int c=R[0];
        for(int i=R[0]; i!=0; i=R[i])if(S[i]<S[c])c=i;
        remove(c);//找到列中包含1最多的列因为这样有助于减少递归深度(1多了删掉的行也多矩阵缩小得快)
        for(int i=U[c]; i!=c; i=U[i]){//扫一次被删列中‘1’的编号,注意remove未对c列有任何操作(只改两侧)
            ans[d]=Row[i];//本次搜索的深度就是这次‘1’所在的行了
            for(int j=R[i]; j!=i; j=R[j])remove(Col[j]);//然后删除该列及列中为1的行
            if(dance(d+1))return true;//继续搜索
            for(int j=L[i]; j!=i; j=L[j])resume(Col[j]);//如不能成功则依次恢复
        }
        resume(c);//每次搜索完如果不能成功就要回复这一列
        return false;
    }
}dlx;
int main(){
    int n,m,num,j;
    while(scanf("%d%d",&n,&m)!=EOF){
        dlx.init(n,m);
        for(int i=1; i<=n; i++){
            scanf("%d",&num);
            while(num--){
                scanf("%d",&j);
                dlx.Link(i,j);
            }
        }
        if(!dlx.dance(0)) printf("NO\n");
        else{
            printf("%d",dlx.ansd);
            for(int i=0; i<dlx.ansd; i++)
                printf(" %d",dlx.ans[i]);
            printf("\n");
        }
    }
    return 0;
}

HUST 1017 Exact cover(舞蹈链 入门题)
题意:每一行的某些列给定为1,现在问是否能找某些行,使得每一列的1出现一次
样例输入
6 7
3 1 4 7
2 1 4
3 4 5 7
3 3 5 6
4 2 3 6 7
2 2 7
样例输出
3 2 4 6

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值