DLX 精确覆盖 -- 数独裸题 Sudoku POJ - 3074

Sudoku POJ - 3074

转自:https://www.cnblogs.com/wust-ouyangli/p/5750581.html

题意:
给出一个9*9的矩阵,有一些格子已经填了数,有一些是.代表未填。求任意一组解使得每行包含1 ~ 9,每列包含1 ~ 9,每个小矩形(3 * 3)包含1 ~ 9。

解析:
精确覆盖DLX的经典题目,每一行代表要填数的情况,列共有81*4列,第一个81行代表第i行j列放了数,第二个81列代表第i行放的数k,第三个81列代表第j列放的数k,第四个81行代表第i个小矩形放的数k。对于字符为 . 的情况添加9行,对于字符为数字的情况添加一行。然后就是跑一遍DLX,保存一下答案。

code:

#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int INF=1e9+7;
const int ms=81*10;
const int maxn=ms*4;
int ans[maxn];
struct DLX
{
    int n,id;
    int L[maxn],R[maxn],U[maxn],D[maxn];
    int C[maxn],S[maxn],loc[maxn][3];
    int H[ms];
    void init(int nn=0)
    {
        n=nn;
        for(int i=0;i<=n;i++) U[i]=D[i]=i,L[i]=i-1,R[i]=i+1;
        L[0]=n; R[n]=0;
        id=n;
        memset(S,0,sizeof(S));
        memset(H,-1,sizeof(H));
    }
    void Link(int x,int y,int px,int py,int k)
    {
        ++id;
        D[id]=y; U[id]=U[y];
        D[U[y]]=id; U[y]=id;
        loc[id][0]=px,loc[id][1]=py,loc[id][2]=k;
        C[id]=y; S[y]++;
        if(H[x]==-1) H[x]=L[id]=R[id]=id;
        else
        {
            int a=H[x];
            int b=R[a];
            L[id]=a; R[a]=id;
            R[id]=b; L[b]=id;
            H[x]=id;
        }
    }
    void Remove(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[C[j]]--;
        }
    }
    void Resume(int c)
    {
        for(int i=U[c];i!=c;i=U[i])
            for(int j=R[i];j!=i;j=R[j])
        {
            S[C[j]]++;
            U[D[j]]=j;
            D[U[j]]=j;
        }
        L[R[c]]=c;
        R[L[c]]=c;
    }
    bool dfs(int step)
    {
        if(step==81) return true;
        if(R[0]==0) return false;
        int Min=INF,c=-1;
        for(int i=R[0];i;i=R[i])
            if(Min>S[i]){ Min=S[i]; c=i; }
        Remove(c);
        for(int i=D[c];i!=c;i=D[i])
        {
            ans[step]=i;
            for(int j=R[i];j!=i;j=R[j]) Remove(C[j]);
            if(dfs(step+1)) return true;
            for(int j=L[i];j!=i;j=L[j]) Resume(C[j]);
        }
        Resume(c);
        return false;
    }
}dlx;
int main()
{
    char S[90];
    while(scanf("%s",S)!=EOF)
    {
        if(S[0]=='e') break;
        dlx.init(81*4);
        int k=0,r=0;
        for(int x=0;x<9;x++)
            for(int y=0;y<9;y++)
        {
            char ch=S[k++];
            int a,b,c,d;
            if(ch=='.')
            {
                for(int i=1;i<=9;i++)
                {
                    a=x*9+y+1;
                    b=x*9+i+81;
                    c=y*9+i+81+81;
                    int s=(x/3)*3+y/3;
                    d=s*9+i+81+81+81;
                    ++r;
                    dlx.Link(r,a,x,y,i);
                    dlx.Link(r,b,x,y,i);
                    dlx.Link(r,c,x,y,i);
                    dlx.Link(r,d,x,y,i);
                }
            }
            else
            {
                int i=ch-'0';
                a=x*9+y+1;
                b=x*9+i+81;
                c=y*9+i+81+81;
                int s=(x/3)*3+y/3;
                d=s*9+i+81+81+81;
                ++r;
                dlx.Link(r,a,x,y,i);
                dlx.Link(r,b,x,y,i);
                dlx.Link(r,c,x,y,i);
                dlx.Link(r,d,x,y,i);
            }
        }
        dlx.dfs(0);
        int res[10][10];
        for(int i=0;i<81;i++)
        {
            int a=ans[i];
            int x=dlx.loc[a][0],y=dlx.loc[a][1],k=dlx.loc[a][2];
            res[x][y]=k;
        }
        for(int i=0;i<9;i++)
            for(int j=0;j<9;j++) printf("%d",res[i][j]);
        printf("\n");
    }
    return 0;
}

Squiggly Sudoku HDU - 4069

题意:
一个数独求解,与一般数独不同的是此数独用墙把81个数分成了9 个宫,每个宫都有1到9 。样例中每个数独的数最多是由四个墙和本身的数组成
对于输出,数独无解则输出无解,有多个解则输出多个解,右一个解则输出补全的数独。

思路:
与一般的数独解法大致相同,不同的是此题首先用搜索将数独中的数分好宫,然后是当有解时,保存答案。

code:

#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <iostream>
#define mod 1000000007
#define eps 1e-6
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;

const int maxn=10240;
struct DLX
{
    int n,id;
    int L[maxn],R[maxn],U[maxn],D[maxn];
    int C[maxn],S[maxn],loc[maxn][3];//C代表列,S代表每列有的数字数量,loc代表这个数在数独中的位置和数值
    int H[maxn],ans[maxn],ansed;//ansed表示解的个数,ans记录答案
    int fans[maxn],fansed;//复制的ansed,ans数组
    void init(int nn)
    {
        n=nn;
        for(int i=0;i<=n;i++)
        {
            U[i]=D[i]=i;
            L[i]=i-1;
            R[i]=i+1;
        }
        L[0]=n; R[n]=0;
        id=n;
        ansed=0;
        fansed=0;
        memset(S,0,sizeof(S));
        memset(H,-1,sizeof(H));
    }
    void Link(int x,int y,int px,int py,int k)
    {
        ++id;
        D[id]=y; U[id]=U[y];
        D[U[y]]=id; U[y]=id;
        loc[id][0]=px,loc[id][1]=py,loc[id][2]=k;//存放数的位置和数
        C[id]=y;
        S[y]++;//此列1的数量加一
        if(H[x]==-1) H[x]=L[id]=R[id]=id;
        else
        {
            int a=H[x];
            int b=R[a];
            L[id]=a; R[a]=id;
            R[id]=b; L[b]=id;
            H[x]=id;
        }
    }
    void Remove(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[C[j]]--;
             }
        }
    }
    void Resume(int c)
    {
        for(int i=U[c];i!=c;i=U[i])
            for(int j=R[i];j!=i;j=R[j])
        {
            S[C[j]]++;
            U[D[j]]=j;
            D[U[j]]=j;
        }
        L[R[c]]=c;
        R[L[c]]=c;
    }
    void fuzhi()//复制已经得到的数独,
    {
        fansed=ansed;
        for(int i=0;i<81;i++)
        {
            fans[i]=ans[i];
        }
    }
    void  dfs(int step)
    {
        if(R[0]==0&&step==81)
        {
            ansed++;
            fuzhi();
            return ;
        }
        int c=R[0];
        for(int i=R[0];i;i=R[i])//优先循环1的数量少的一列
        {
             if(S[i]<S[c])
             { 
                 c=i;
             }
        }
        Remove(c);
        for(int i=D[c];i!=c;i=D[i])
        {
            ans[step]=i;
            for(int j=R[i];j!=i;j=R[j]) Remove(C[j]);
            dfs(step+1);
            if(ansed>=2)//当解多于两个时已是多解,不需要再循环了
            {
                return ;
            }
            for(int j=L[i];j!=i;j=L[j]) Resume(C[j]);
        }
        Resume(c);
    }
}dlx;
struct node
{
    int fxi[4],gong;   //fxi表示四个方向,gong表示所在的第几宫  
    int x,y,num;       //x,y表示行列,num表示数 
};
node sd[10][10];
int bj[10][10];
int fx[4]={0,1,0,-1},fy[4]={-1,0,1,0};
void bfs(int x,int y,int z) //搜索同一宫的位置 
{
    queue<node> qu;
    node no=sd[x][y];
    bj[x][y]=1;
    qu.push(no);
    while(!qu.empty())
    {
        no=qu.front();
        qu.pop();
        for(int k=0;k<4;k++)
        {
            int i=no.x+fx[k];
            int j=no.y+fy[k];
            if(!no.fxi[k]&&bj[i][j]==0) //没有墙并且没走过 
            {
                sd[i][j].gong=z;
                bj[i][j]=1;
                node s=sd[i][j];
                qu.push(s);
            }
        }
    }
}
int main()
{
    int t,ans=0;  //ans表示第几个测试任务
    scanf("%d",&t);
    while(t--)
    {
        ans++;
        memset(sd,0,sizeof(sd));
        int js=0;
        dlx.init(81*4);
        for(int i=0;i<9;i++)
        {
            for(int j=0;j<9;j++)
            {
                scanf("%d",&sd[i][j].num);
                sd[i][j].x=i;
                sd[i][j].y=j;
                if(sd[i][j].num>=128)//左
                {
                    sd[i][j].num-=128;
                    sd[i][j].fxi[0]=1;
                }
                 if(sd[i][j].num>=64)//下
                {
                    sd[i][j].num-=64;
                    sd[i][j].fxi[1]=1;
                }
                 if(sd[i][j].num>=32)//右
                {
                    sd[i][j].num-=32;
                    sd[i][j].fxi[2]=1;
                }
              if(sd[i][j].num>=16)//上
                {
                    sd[i][j].num-=16;
                    sd[i][j].fxi[3]=1;
                }
            }
        }
        js=0;
        memset(bj,0,sizeof(bj));
        for(int i=0;i<9;i++)
        {
            for(int j=0;j<9;j++)
            {
                if(bj[i][j]==0)//循环所有数独
                {
                    bfs(i,j,js);
                    sd[i][j].gong=js;
                    js++;
                }
            }
        }
        for(int i=0;i<9;i++)
        {
            for(int j=0;j<9;j++)
            {
                int a,b,c,d;
                if(sd[i][j].num==0)
                {
                    for(int k=1;k<=9;k++)
                    {
                        a = i*9+j+1;
                        b = i*9+k+81;
                        c = j*9+k+81+81;
                        d = sd[i][j].gong*9+k+81+81+81;
                        js++;
                        dlx.Link(js,a,i,j,k);
                        dlx.Link(js,b,i,j,k);
                        dlx.Link(js,c,i,j,k);
                        dlx.Link(js,d,i,j,k);
                    }
                }
                else
                {
                    int k=sd[i][j].num;
                    a=i*9+j+1;
                    b=i*9+k+81;
                    c=j*9+k+81+81;
                    d=sd[i][j].gong*9+k+81+81+81;
                    js++;
                    dlx.Link(js,a,i,j,k);
                    dlx.Link(js,b,i,j,k);
                    dlx.Link(js,c,i,j,k);
                    dlx.Link(js,d,i,j,k);
                }
            }
        }
        dlx.dfs(0);
        int num=dlx.fansed;
        if(num==0)//无解
        {
            printf("Case %d:\nNo solution\n",ans);
        }
        else if(num>1)//多解
        {
            printf("Case %d:\nMultiple Solutions\n",ans);
        }
        else//一个解
        {
            int no[10][10];
            for(int i=0;i<81;i++)
            {
                int a=dlx.fans[i];
                int x=dlx.loc[a][0];
                int y=dlx.loc[a][1];
                int k=dlx.loc[a][2];
                no[x][y]=k;
            }
            printf("Case %d:\n",ans);
            for(int i=0;i<9;i++)
            {
                for(int j=0;j<9;j++)
                {
                    printf("%d",no[i][j]);
                }
                printf("\n");
            }
        }
    }
}
发布了92 篇原创文章 · 获赞 7 · 访问量 3748

猜你喜欢

转载自blog.csdn.net/dajiangyou123456/article/details/104155865