洛谷 P4929 【模板】舞蹈链(DLX)

题目链接

DLX例题(洛谷模板题就是省选难度了qwq)。

DLX的具体思想我就不赘述了,网上有很多讲的不错的。下附链接

网站1网站2网站3网站4

AC代码如下(代码思路就看注释吧……)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define mx 250505 
using namespace std;
int n,m,cnt;
int l[mx],r[mx],u[mx],d[mx],row[mx],col[mx];//定义一个点的左右上下以及行数列数。
int h[mx],s[mx];//一行的第一个数 ,一列有几个数
int ansk[mx];//答案 
inline void Init(int m){//初始化m列。m+1个数,0为head(无实际意义,是最后用来判断的 
    for(int i=0;i<=m;i++){
        r[i]=i+1;
        l[i]=i-1;
        u[i]=d[i]=i; 
    }
    r[m]=0,l[0]=m;
    memset(h,-1,sizeof(h));
    memset(s,0,sizeof(s));
    cnt=m+1;//之后录入的数从m+1的下标开始,且1~m的列标在最下面 
}
inline void link(int R,int C){//插入一个在R行C列的点 
    s[C]++;
    row[cnt]=R,col[cnt]=C;//记录行和列 
    d[cnt]=C;//cnt的下面为c,即1~m 
    u[cnt]=u[C];//cnt的上面变为原先C的上面面 
    d[u[C]]=cnt;//原先c的上面的下面变为cnt 
    u[C]=cnt; //c的上面变为cnt。一系列操作即将cnt插入C和C下面一个数之间,让cnt成为最接近c的那个数
    if(h[R]==-1)//如果这一行没有数的话就将该数作为第一个数录入 
        h[R]=r[cnt]=l[cnt]=cnt;
    else{//有数的话 
        l[cnt]=h[R];//将cnt的左边连接到h[R]的右边 
        r[cnt]=r[h[R]];//将cnt的右边连接到h[R]的右边 
        l[r[h[R]]]=cnt;//将原来h[R]的右边连接到cnt的右边 
        r[h[R]]=cnt;//将cnt连接到h[R]的右边
    }     
     cnt++;
     return;
}
inline void remove(int C){//删除c列和c列上有点的行 
    r[l[C]]=r[C],l[r[C]]=l[C];//删除这一列的存在。
    for(int i=u[C];i!=C;i=u[i])//找每个在该列上的数 
        for(int j=r[i];j!=i;j=r[j]){//找该列上的数所在的那行 ,并删除整行 
            u[d[j]]=u[j];
            d[u[j]]=d[j];
            s[col[j]]--;//该数所在列内数量-1
        } 
    return;
} 
inline void resume(int C){//恢复c列和c列上有点的行 
    r[l[C]]=C,l[r[C]]=C;
    for(int i=u[C];i!=C;i=u[i])
        for(int j=r[i];j!=i;j=r[j]){
            u[d[j]]=j;
            d[u[j]]=j;
            s[col[j]]++; 
        }
    return;
} 
bool dance(int deep){
    if(r[0]==0){//如果列删光了且除了head没有元素留下来。则表示该情况成立 
        for(int i=0;i<deep;i++)
            printf("%d ",ansk[i]);
            return 1;
    }
    int c=r[0];
    for(int i=r[0];i!=0;i=r[i])//优先从列中数最少的开始找。缩小查找范围 
        if(s[i]<s[c])
            c=i;
    remove(c);//删除c列 
    for(int i=d[c];i!=c;i=d[i]){//从c列当中选1行进行查找 
        ansk[deep]=row[i];//记录当前被删的行数
        for(int j=r[i];j!=i;j=r[j])//删除被c列i行影响而不能选择的列 
            remove(col[j]);
        if(dance(deep+1))
            return 1;
        for(int j=l[i];j!=i;j=l[j])
            resume(col[j]);
    } 
    resume(c);
    return 0;
}
int main(){
    scanf("%d%d",&n,&m);
    Init(m); 
    char temp;
    int k;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            scanf("%d",&k);
             if(k)
                 link(i,j);
        }
    if(!dance(0))
        printf("No Solution!");
    return 0;
}

 

posted @ 2021-02-26 20:24  mikku  阅读(68)  评论(0)    收藏  举报