hdu1426 DLX

题意:给定了一个没有填完的数独,然后输出最后的解

解法:暴力dfs可搞 但是不能枉费经典的跳舞链模型 

  一共是81*9行代表了每一个格子填的数 一共81*4列分别代表了4中约束条件

1 每一行必须有数字

2 每一行的数字必须是1-9

3 每一列的数字必须是1-9

4 每一个九宫格的数字必须是1-9

   然后建跳舞链直接跑即可

#include<cstdio>
#include<iostream>
using namespace std;
#define maxr (9*9*9+11)
#define maxc (9*9*4+11)
int l[maxc+(maxr<<2)],r[maxc+(maxr<<2)],U[maxc+(maxr<<2)],D[maxc+(maxr<<2)];
int tot[maxc],nRow[maxc+maxr*5],nCol[maxc+maxr*5];
int head[10][10][10],cnt,mp[10][10];
bool scan(){
    char ch[90];
    for (int i=1;i<=9;i++)
        for (int j=1;j<=9;j++){
            if(!~scanf("%s",ch))return 0;
            if(*ch=='?')mp[i][j]=0;
            else mp[i][j]=*ch-'0';
        }
    return 1;
}
int sub(int i,int j){
    --i;--j;
    return (i/3)*3+(j/3+1);
}
void ins(int c,int cnt){
    U[D[c]]=cnt;
    D[cnt]=D[c];
    U[cnt]=c;
    D[c]=cnt;
    tot[c]++;
    nCol[cnt]=c;
}
void kill(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];
            tot[nCol[j]]--;
        }
}
void reborn(int c){
    for(int i=U[c];i!=c;i=U[i])
        for(int j=l[i];j!=i;j=l[j]){
            
            U[D[j]]=D[U[j]]=j;
            tot[nCol[j]]++;
        }
    l[r[c]]=r[l[c]]=c;
}
int dfs(int k){
    if(k>81)return 1;
    int c=0,mi=INT_MAX;
    for(int i=r[0];i!=0;i=r[i]){
        if(!tot[i])return 0;
        if(tot[i]<mi){
            mi=tot[i];
            c=i;
        }
    }
    kill(c);
    for(int i=D[c];i!=c;i=D[i]){
        int tmp=nRow[i];
        mp[tmp/100][(tmp/10)%10]=tmp%10;
        for(int j=r[i];j!=i;j=r[j])
            kill(nCol[j]);
        if(dfs(k+1))return 1;
        for(int j=l[i];j!=i;j=l[j])
            reborn(nCol[j]);
    }
    reborn(c);
    return 0;
}
int main()
{
    int cas=1;
    while(1){
        
        if(!scan())break;
        
        if(cas!=1)printf("\n");
        cas++;
        //init
        for(int i=0;i<=(81<<2);++i){
            tot[i]=0;l[i]=i-1;r[i]=i+1;
            U[i]=D[i]=i;
            nCol[i]=0;
        }l[0]=81*4;r[81*4]=0;
        
        cnt=81*4;
        
        for(int i=1;i<=9;i++)
            for(int j=1;j<=9;j++){
                
                if(mp[i][j]){
                    int k=mp[i][j];
                    for (int u=1;u<=4;u++){
                        l[cnt+u]=cnt+u-1;
                        r[cnt+u]=cnt+u+1;
                        nRow[cnt+u]=100*i+10*j+k;
                    }
                    l[cnt+1]=cnt+4; r[cnt+4]=cnt+1;
                    head[i][j][k]=cnt+1;
                    ins((i-1)*9+j,cnt+1);
                    ins(81+(i-1)*9+k,cnt+2);
                    ins(81*2+(j-1)*9+k,cnt+3);
                    ins(81*3+(sub(i,j)-1)*9+k,cnt+4);
                    cnt+=4;
                }
                else for(int k=1;k<=9;k++){
                        for(int u=1;u<=4;u++){
                            l[cnt+u]=cnt+u-1;
                            r[cnt+u]=cnt+u+1;
                            nRow[cnt+u]=100*i+10*j+k;
                        }
                        l[cnt+1]=cnt+4; r[cnt+4]=cnt+1;
                        head[i][j][k]=cnt+1;
                        ins((i-1)*9+j,cnt+1);
                        ins(81+(i-1)*9+k,cnt+2);
                        ins(81*2+(j-1)*9+k,cnt+3);
                        ins(81*3+(sub(i,j)-1)*9+k,cnt+4);
                        cnt+=4;
                    }
            }
        int k=0;
        for(int i=1;i<=9;i++)
            for(int j=1;j<=9;j++)
                if(mp[i][j]){
                    k++;
                    kill(nCol[head[i][j][mp[i][j]]]);
            for(int u=r[head[i][j][mp[i][j]]];u!=head[i][j][mp[i][j]];u=r[u])
                        kill(nCol[u]);
                }
        
        
        dfs(k+1);
        
        
        for(int i=1;i<=9;++i,printf("\n"))
            for(int j=1;j<=9;++j)
                printf(j==1?"%d":" %d",mp[i][j]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值