Vision_数据结构_舞蹈连

///定义(ctrl+c):
/*
    (1)历史:
       X算法是高德纳提出的解决精确覆盖问题的算法,而dancing links X算法则是DonKnuth(《计算机程序设计艺术》
         的作者)提出的对X算法的一种高效实现,这种实现建立在如上所说的矩阵表示法上。


    (2)算法思想:
        由如上精确覆盖问题的矩阵表示法中,我们知道dancing links x 是用来求解一个 01矩阵中选取哪几行可以使得
        这几行每一列都有且仅有一个1(就是每个元素在这几个子集里有且仅有出现过一次)。先不管他的实际意义,
        我们需要做的就是在一个01矩阵的选取某几行使之符合上述条件。我们很容易就想到枚举,然后判断符不符合条件,
        但是这个做法实在是太消耗时间。dacing links x就是一个高效的求解该类问题的算法,而这种算法,基于交叉十字
        循环双向链的数据结构。
    (3)精确覆盖问题的定义:
        给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1
    (4)重复覆盖问题:
        即选取一个01矩阵中的几行,使这几行组成的新矩阵的每一列至少有一个1。 
        该问题在精确覆盖问题上减少了一个约束条件
    (5)应用:
        解决精确覆盖问题和重复覆盖问题
    
*/
///代码:


/*
**name:舞蹈连-精确覆盖问题
**function:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1
**输入参数:包含1的位置(i,j)
**输出参数:行集合的信息
*/
#include <iostream>
#include <cstdio>
using namespace std;

const int maxnode = 100010;
const int maxm = 1010;
const int maxn = 1010;

struct DLX{
    int n,m,len;
    int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];
    int H[maxn];//行头结点
    int S[maxm];//每列有多少个结点
    int ansd,ans[maxn];//如果有答案,则选了ansd行,具体是哪几行放在ans[]数组里面,ans[0~ansd-1]

    void init(int _n,int _m){
        n = _n;m = _m;
        for(int i = 0; i <= m; i++){
            S[i] = 0;
            U[i] = D[i] = i;//初始状态时,上下都指向自己
            L[i] = i-1;
            R[i] = i+1;
        }
        R[m] = 0,L[0] = m;
        len = m;//编号,每列都有一个头结点,编号1~m
        for(int i = 1; i <= n; i++)
            H[i] = -1;//每一行的头结点
    }

    void link(int r,int c){//第r行,第c列
        ++S[Col[++len]=c];//第len个节点所在的列为c,当前列的结点数++
        Row[len] = r;//第len个结点行位置为r
        D[len] = D[c];
        U[D[c]] = len;
        U[len] = c;
        D[c] = len;
        if(H[r] < 0)
            H[r] = L[len] = R[len] = len;
        else{
            R[len] = R[H[r]];
            L[R[H[r]]] = len;
            L[len] = H[r];
            R[H[r]] = len;
        }
    }

    void del(int c){//删除结点c,以及c上下结点所在的行
        //每次调用这个函数,都是从列头节点开始向下删除,这里c也可以理解为第c列
        //因为第c列的列头节点编号为c
        L[R[c]] = L[c];
        R[L[c]] = R[c];
        for(int i = D[c]; i != c; i = D[i]){
            for(int j = R[i]; j != i; j = R[j]){
                U[D[j]] = U[j];
                D[U[j]] = D[j];
                --S[Col[j]];
            }
        }
    }

    void resume(int c){//回复结点c,以及c上下节点所在的行(同上,也可以理解为从第c列的头节点开始恢复
        for(int i = U[c]; i != c; i = U[i]){
            for(int j = L[i]; j != i; j = L[j]){
                ++S[Col[U[D[j]]=D[U[j]]=j]];
            }
        }
        L[R[c]] = R[L[c]] = c;
    }

    bool dance(int 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;
        }

        del(c);//找到结点数最少的列,当前元素不是原图上0,1的节点,而是列头节点
        for(int i = D[c]; i != c; i = D[i]){
            ans[d] = Row[i];//列头节点下面的一个节点
            for(int j = R[i]; j != i; j = R[j])
                del(Col[j]);
            if(dance(d+1))//找到,返回
                return true;
            for(int j = L[i]; j != i; j = L[j])
                resume(Col[j]);
        }
        resume(c);
        return false;
    }
};

DLX head;

int main(){
    int n,m,k;
    while(~scanf("%d%d%d",&n,&m,&k)){
        head.init(n,m);
        for(int i = 1,x; i <= n; i++){
            for(int j = 1;j<=m;j++){
                scanf("%d",&x);
                if(!x)head.link(i,j);
            }
        }

        if(!head.dance(0))
            printf("NO\n");
        else{
            printf("%d\n",head.ansd);
            for(int i = 0; i < head.ansd; i++)
                printf(" %d",head.ans[i]);
            printf("\n");
        }
    }
    return 0;
}



/*
**name: 舞蹈连-重复覆盖问题
**function: 即选取一个01矩阵中的几行,使这几行组成的新矩阵的每一列至少有一个1。 
**输入参数:包含1的位置(i,j)
**输出参数:至少所消耗的行数,如果返回无穷大则表明不存在
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
struct DLX
{
    int n,m,len,ansd;
    int S[400],U[200000],D[200000],L[200000],R[200000],Row[200000],Col[200000];
    int H[400];

    void init(int _n,int _m)
    {
        n=_n;
        m=_m;
        len=m;
        ansd=0x3f3f3f3f;
        for(int i=0; i<=m; i++)
        {
            S[i]=0;
            U[i]=D[i]=i;
            R[i]=i+1;
            L[i]=i-1;
            Row[i]=0;
            Col[i]=i;
        }
        R[m]=0;
        L[0]=m;
        for(int i=1; i<=n; i++)
            H[i]=-1;
    }

    void link(int r,int c)
    {
        ++len;
        ++S[c];
        Row[len]=r;
        Col[len]=c;
        D[len]=D[c];
        U[D[c]]=len;
        U[len]=c;
        D[c]=len;
        if(H[r]==-1)
        {
            H[r]=R[len]=L[len]=len;
        }
        else
        {
            R[len]=R[H[r]];
            L[R[H[r]]]=len;
            L[len]=H[r];
            R[H[r]]=len;
        }
    }
    int f()
    {
        int ans=0;
        int V[400];
        memset(V,0,sizeof(V));
        for(int i=R[0]; i!=0; i=R[i])
        {
            V[i]=true;
        }
        for(int i=R[0]; i!=0; i=R[i])
        {
            if(V[i])
            {
                ans++;
                V[i]=false;
                for(int j=D[i]; j!=i; j=D[j])
                {
                    for(int k=R[j]; k!=j; k=R[k])
                    {
                        V[Col[k]]=false;
                    }
                }

            }
        }
        return ans;
    }

    void del(int c)
    {
        for(int i=D[c]; i!=c; i=D[i])
        {
            R[L[i]]=R[i];
            L[R[i]]=L[i];
        }
    }
    void resume(int c)
    {
        for(int i=U[c]; i!=c; i=U[i])
        {
            L[R[i]]=i;
            R[L[i]]=i;
        }
    }
    void Dance(int d)
    {
        if(d>=ansd)return ;
        if(d+f()>=ansd)return ;
        if(R[0]==0)
        {
            ansd=min(ansd,d);
            return ;
        }
        int c=R[0];
        for(int i=R[0]; i!=0; i=R[i])
        {
            if(S[i]<S[c])
                c=i;
        }

        for(int i=D[c]; i!=c; i=D[i])
        {
            del(i);
            for(int j=R[i]; j!=i; j=R[j])del(j);
            Dance(d+1);
            for(int j=L[i]; j!=i; j=L[j])resume(j);
            resume(i);
        }
        return ;
    }
};
DLX head;
int main(){
    int n,m,k,x;
    while(~scanf("%d%d%d",&n,&m,&k)){
        head.init(n,m);
        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=m;j++){
                scanf("%d",&x);
                if(!x)head.link(i,j);
            }
        }
        head.Dance(0);
        if(head.ansd<=k&&head.ansd!=0x3f3f3f3f)printf("yes\n");
        else printf("no\n");
        //printf("%d\n",head.ansd);
    }
    return 0;
}



/*
**name:舞蹈连-精确覆盖问题
**function:求解数独
**输入参数:数独盘的字符串
**输出参数:完整的数独盘字符串
*/
#include <cstring>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <cmath>
#include <algorithm>

using namespace std;
const int maxn=1000,maxm=400,maxnode=400000;
struct DLX
{
    int n,m,len;
    int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];
    int H[maxn];///行头结点
    int S[maxm];///每列有多少个结点
    int ansd,ans[maxn];

    void init(int _n,int _m)
    {
        n=_n;
        m=_m;
        for(int i=0; i<=m; i++)
        {
            S[i]=0;
            U[i]=D[i]=i;
            L[i]=i-1;
            R[i]=i+1;
        }
        R[m]=0,L[0]=m;
        len=m;
        for(int i=1; i<=n; i++)
        {
            //cout<<"**"<<endl;
            H[i]=-1;
        }
    }

    void link(int r,int c)
    {
        ++S[Col[++len]=c];
        Row[len]=r;
        D[len]=D[c];
        U[D[c]]=len;
        U[len]=c;
        D[c]=len;
        if(H[r]<0)
            H[r]=L[len]=R[len]=len;
        else
        {
            R[len]=R[H[r]];
            L[R[H[r]]]=len;
            L[len]=H[r];
            R[H[r]]=len;
        }
    }

    void del(int c)
    {
        L[R[c]]=L[c];
        R[L[c]]=R[c];
        for(int i=D[c]; i!=c; i=D[i])
        {
            for(int j=R[i]; j!=i; j=R[j])
            {
                U[D[j]]=U[j];
                D[U[j]]=D[j];
                --S[Col[j]];
            }
        }
    }

    void resume(int c)
    {
        for(int i=U[c]; i!=c; i=U[i])
        {
            for(int j=L[i]; j!=i; j=L[j])
            {
                ++S[Col[U[D[j]]=D[U[j]]=j]];
            }
        }
        L[R[c]]=R[L[c]]=c;
    }
    bool dance(int d)
    {
        //cout<<d<<endl;
        if(R[0]==0)
        {
            ansd=d;
            return true;
        }
        int c=R[0];
        ///找一个1少的列,相当于剪枝了
        for(int i=R[0]; i!=0; i=R[i])
        {
            //cout<<S[i]<<endl;
            if(S[i]<S[c])
            {
                c=i;
            }
        }
        del(c);
        //cout<<S[1]<<endl;
        //cout<<"del()"<<c<<endl;
        //cout<<D[c]<<" "<<endl;
        for(int i=D[c]; i!=c; i=D[i])
        {
            //cout<<i<<endl;
            ans[d]=Row[i];
            for(int j=R[i]; j!=i; j=R[j])
            {
                del(Col[j]);
            }
            if(dance(d+1))return true;
            for(int j=L[i]; j!=i; j=L[j])
            {
                resume(Col[j]);
            }
        }
        resume(c);
        return false;
    }
};
struct node
{
    int pos;
    int data;
    int lie;
} q[2000];
DLX head;
int main()
{
    char s[100];
    while(scanf("%s",s))
    {
        if(strcmp(s,"end")==0)break;
        int len=strlen(s);
        head.init(1000,324);
        int row=0;
        for(int i=0; i<len; i++)
        {
            int x=i/9,y=i%9;
            if(s[i]=='.')
            {
                for(int j=1; j<=9; j++)
                {
                    //x行y列填了一个数字
                    head.link(++row,x*9+(y+1));
                    //x行填了一个数字j
                    head.link(row,x*9+j+81);
                    //y列填了一个数字j
                    head.link(row,y*9+j+162);
                    //g宫填了一个数字j
                    int xx=x/3*3,yy=y/3*3;
                    int g=xx/3*3+yy/3+1;
                    head.link(row,(g-1)*9+j+243);
                    q[row].pos=i;
                    q[row].data=j;
                    q[row].lie=x*9+y+1;
                }
            }
            else
            {
                int a=s[i]-'0';
                //x行y列填了一个数字
                head.link(++row,x*9+(y+1));
                //x行填了一个数字j
                head.link(row,x*9+a+81);
                //y列填了一个数字j
                head.link(row,y*9+a+162);
                //g宫填了一个数字j
                int xx=x/3*3,yy=y/3*3;
                int g=xx/3*3+yy/3+1;
                head.link(row,(g-1)*9+a+243);
                q[row].pos=i;
                q[row].data=a;
                q[row].lie=x*9+y+1;
            }
        }
        //cout<<head.S[1]<<endl;
        bool ok=head.dance(0);
        //cout<<ok<<endl;
        //cout<<head.ansd<<endl;
        for(int i=0; i<head.ansd; i++)
        {
            if(s[q[head.ans[i]].pos]=='.')s[q[head.ans[i]].pos]='0'+q[head.ans[i]].data;
        }
        //cout<<endl;
        printf("%s\n",s);
    }
    return 0;
}



///扩展:
/*
    舞蹈连的步骤:
        1、从矩阵中选择一行
        2、根据定义,标示矩阵中其他行的元素
        3、删除相关行和列的元素,得到新矩阵
        4、如果新矩阵是空矩阵,并且之前的一行都是1,那么求解结束,跳转到6;新矩阵不是空矩阵,
    继续求解,跳转到1;新矩阵是空矩阵,之前的一行中有0,跳转到5
        5、说明之前的选择有误,回溯到之前的一个矩阵,跳转到1;如果没有矩阵可以回溯,说明该问题无解,跳转到7
        6、求解结束,把结果输出
        7、求解结束,输出无解消息
*/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值