POJ_3074_Sudoku(DancingLinksX精确覆盖)

来源:互联网 发布:陈田村拆车件淘宝店 编辑:程序博客网 时间:2024/04/30 13:23

Sudoku
Time Limit: 1000MS Memory Limit: 65536KTotal Submissions: 9412 Accepted: 3375

Description

In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For example,

.2738..1..1...6735.......293.5692.8...........6.1745.364.......9518...7..8..6534.

Given some of the numbers in the grid, your goal is to determine the remaining numbers such that the numbers 1 through 9 appear exactly once in (1) each of nine 3 × 3 subgrids, (2) each of the nine rows, and (3) each of the nine columns.

Input

The input test file will contain multiple cases. Each test case consists of a single line containing 81 characters, which represent the 81 squares of the Sudoku grid, given one row at a time. Each character is either a digit (from 1 to 9) or a period (used to indicate an unfilled square). You may assume that each puzzle in the input will have exactly one solution. The end-of-file is denoted by a single line containing the word “end”.

Output

For each test case, print a line representing the completed Sudoku puzzle.

Sample Input

.2738..1..1...6735.......293.5692.8...........6.1745.364.......9518...7..8..6534.......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.end

Sample Output

527389416819426735436751829375692184194538267268174593643217958951843672782965341416837529982465371735129468571298643293746185864351297647913852359682714128574936

Source

Stanford Local 2006



DancingLinksX貌似很喜欢解决数独问题

标准数独每格填写一个数字

每行有1~9各一个

每列有1~9各一个

每小九宫有1~9各一个

正好这4个问题都要覆盖!


因此解法

1~927行代表在i格j列填k

1~81列代表i行有数字j

82~162列代表i列有数字j

163~243列代表i宫有数字j

244~324列代表i行j列有一个数字

前三个行好想,最后一个条件尤为重要!

前三个条件其实是不充分的,不能够保证所选取的方案(在i格j列填k)可行,即可能在一个格中多次填数!

而且出于效率考量,第4个条件应该放于第一位。

再有由于每一个行最多(本题解法中精确的对应4个)对应4个列为1

因此如果空间有限,则十字链表节点数可以只开到行数x4+列数即可


手写了一大堆的删除不必要行列的算法,但是对于精确一解的数独这并不很奏效

会TLE,因为处理不必要行列过程较久

代码如下

#include <iostream>#include <stdio.h>#include <string.h>using namespace std;const int MN=9*9*9+10;const int MM=9*9*4+10;const int MNN=MN*MM+MM; //最大点数//int li[MN][MM];struct DLX{    int n,m,si;//n行数m列数si目前有的节点数    //十字链表组成部分    int U[MNN],D[MNN],L[MNN],R[MNN],Row[MNN],Col[MNN];    //第i个结点的U向上指针D下L左R右,所在位置Row行Col列    int H[MN],S[MM]; //记录行的选择情况和列的覆盖情况    int ansd,ans[MN];    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;        si=m;                 //目前用了前0~m个结点        for(int i=1;i<=n;i++)            H[i]=-1;    }    void link(int r,int c)    //插入点(r,c)    {//        li[r][c]=1;//        cout<<"link "<<r<<" "<<(r-1)/9/9<<" "<<(r-1)/9%9<<" "<<(r-1)%9+1<<endl;//        if(c<82)//            cout<<"     "<<c<<" hang "<<(c-1)/9<<" shuzi "<<(c-1)%9+1<<endl;//        else if(c>162)//            cout<<"     "<<c<<" gong "<<(c-162-1)/9<<" shuzi "<<(c-162-1)%9+1<<endl;//        else//            cout<<"     "<<c<<" lie "<<(c-81-1)/9<<" shuzi "<<(c-81-1)%9+1<<endl;        ++S[Col[++si]=c];     //si++;Col[si]=c;S[c]++;        Row[si]=r;        D[si]=D[c];        U[D[c]]=si;        U[si]=c;        D[c]=si;        if(H[r]<0)            H[r]=L[si]=R[si]=si;        else        {            R[si]=R[H[r]];            L[R[H[r]]]=si;            L[si]=H[r];            R[H[r]]=si;        }    }    void remove(int c)        //列表中删掉c列    {//        if(c<82)//            cout<<"remove "<<c<<" hang "<<(c-1)/9<<" shuzi "<<(c-1)%9+1<<endl;//        else if(c>162)//            cout<<"remove "<<c<<" gong "<<(c-162-1)/9<<" shuzi "<<(c-162-1)%9+1<<endl;//        else//            cout<<"remove "<<c<<" lie "<<(c-81-1)/9<<" shuzi "<<(c-81-1)%9+1<<endl;        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列    {        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) //主程序从d=0开始,选取了d行    {        //cout<<"dance "<<d<<endl;        if(R[0]==0)//全部覆盖了        {            //全覆盖了之后的操作            ansd=d;            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);        for(int i=D[c];i!=c;i=D[i])        {            ans[d]=Row[i];            for(int j=R[i];j!= i;j=R[j])                remove(Col[j]);            if(dance(d+1))                return 1;            for(int j=L[i];j!=i;j=L[j])                resume(Col[j]);        }        resume(c);        return 0;    }}dlx;int sq[10][10]={    {0,0,0,0,0,0,0,0,0,0},    {0,1,1,1,2,2,2,3,3,3},    {0,1,1,1,2,2,2,3,3,3},    {0,1,1,1,2,2,2,3,3,3},    {0,4,4,4,5,5,5,6,6,6},    {0,4,4,4,5,5,5,6,6,6},    {0,4,4,4,5,5,5,6,6,6},    {0,7,7,7,8,8,8,9,9,9},    {0,7,7,7,8,8,8,9,9,9},    {0,7,7,7,8,8,8,9,9,9}};char ss[100];int isu[MM],ma[MM];int main(){    //freopen("1.in","r",stdin);    //freopen("1.out","w",stdout);    while(1)    {        scanf("%s",ss);        if(ss[0]=='e')            break;        memset(isu,0,sizeof(isu));        memset(ma,0,sizeof(ma));        for(int i=0;i<81;i++)            if(ss[i]!='.')            {                int x=i/9+1;                int y=i%9+1;//1~9                isu[(x-1)*9+ss[i]-'0']=1;                isu[81+(y-1)*9+ss[i]-'0']=1;                isu[162+(sq[x][y]-1)*9+ss[i]-'0']=1;                isu[243+(x-1)*9+y]=1;            }        int nisu=0;        for(int i=1;i<=81*4;i++)            if(!isu[i])                ma[i]=++nisu;        dlx.init(9*9*9,nisu);        for(int i=0;i<81;i++)            if(ss[i]=='.')            {                int x=i/9+1;                int y=i%9+1;//1~9                //if(ma[243+(x-1)*9+y]) //这个格子没有填过                    for(int j=1;j<=9;j++) //在xy格子中填入j                    {                        int f=1;                        if(ma[(x-1)*9+j])                            dlx.link(i*9+j,ma[(x-1)*9+j]);                        else                            f=0;                        if(ma[81+(y-1)*9+j])                            dlx.link(i*9+j,ma[81+(y-1)*9+j]);                        else                            f=0;                        if(ma[162+(sq[x][y]-1)*9+j])                            dlx.link(i*9+j,ma[162+(sq[x][y]-1)*9+j]);                        else                            f=0;                        if(f)                            dlx.link(i*9+j,ma[243+(x-1)*9+y]);                    }            }        //cout<<dlx.dance(0)<<endl;//        for(int j=1;j<=nisu;j++)//            cout<<"\t"<<j;//        cout<<endl;//        for(int i=1;i<=9*9*9;i++)//        {//            cout<<"i  "<<i;//            for(int j=1;j<=nisu;j++)//                if(li[i][j])//                    cout<<"\t"<<j;//            cout<<endl;//        }//        for(int c=1;c<=81*4;c++)//            if(!isu[c])//            {//                cout<<ma[c]<<" ";//                if(c<82)//                    cout<<" hang "<<(c-1)/9<<" shuzi "<<(c-1)%9+1<<endl;//                else if(c<163)//                    cout<<" lie "<<(c-81-1)/9<<" shuzi "<<(c-81-1)%9+1<<endl;//                else if(c<244)//                    cout<<" gong "<<(c-162-1)/9<<" shuzi "<<(c-162-1)%9+1<<endl;//                else//                    cout<<" yong hang"<<(c-243-1)/9<<" lie "<<(c-243-1)%9+1<<endl;//            }////        continue;        dlx.dance(0);        //cout<<dlx.ansd<<endl;        for(int i=0;i<dlx.ansd;i++)        {            int v=(dlx.ans[i]-1)%9+1;            int p=(dlx.ans[i]-1)/9;            ss[p]=v+'0';        }        printf("%s\n",ss);    }    return 0;}


反而倒是比较原始的算法

已经填了数字的格子只加1行

没有填数字的格子加9行的方法,可以轻松通过

#include <iostream>#include <stdio.h>#include <string.h>using namespace std;const int MN=9*9*9+10;const int MM=9*9*4+10;const int MNN=MN*MM+MM; //最大点数struct DLX{    int n,m,si;//n行数m列数si目前有的节点数    //十字链表组成部分    int U[MNN],D[MNN],L[MNN],R[MNN],Row[MNN],Col[MNN];    //第i个结点的U向上指针D下L左R右,所在位置Row行Col列    int H[MN],S[MM]; //记录行的选择情况和列的覆盖情况    int ansd,ans[MN];    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;        si=m;                 //目前用了前0~m个结点        for(int i=1;i<=n;i++)            H[i]=-1;    }    void link(int r,int c)    //插入点(r,c)    {        //cout<<"link r "<<r<<" c "<<c<<endl;        ++S[Col[++si]=c];     //si++;Col[si]=c;S[c]++;        Row[si]=r;        D[si]=D[c];        U[D[c]]=si;        U[si]=c;        D[c]=si;        if(H[r]<0)            H[r]=L[si]=R[si]=si;        else        {            R[si]=R[H[r]];            L[R[H[r]]]=si;            L[si]=H[r];            R[H[r]]=si;        }    }    void remove(int c)        //列表中删掉c列    {        //cout<<"remove "<<c<<endl;        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列    {        //cout<<"resume "<<c<<endl;        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) //选取了d行    {        if(R[0]==0)//全部覆盖了        {            //全覆盖了之后的操作            ansd=d;            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);        for(int i=D[c];i!=c;i=D[i])        {            ans[d]=Row[i];            for(int j=R[i];j!= i;j=R[j])                remove(Col[j]);            if(dance(d+1))                return 1;            for(int j=L[i];j!=i;j=L[j])                resume(Col[j]);        }        resume(c);        return 0;    }}dlx;//可以不用sq矩阵,直接用关系算出在哪个小九宫int sq[9][9]={    {1,1,1,2,2,2,3,3,3},    {1,1,1,2,2,2,3,3,3},    {1,1,1,2,2,2,3,3,3},    {4,4,4,5,5,5,6,6,6},    {4,4,4,5,5,5,6,6,6},    {4,4,4,5,5,5,6,6,6},    {7,7,7,8,8,8,9,9,9},    {7,7,7,8,8,8,9,9,9},    {7,7,7,8,8,8,9,9,9}};char ss[100];int main(){    //freopen("1.in","r",stdin);    //freopen("1.out","w",stdout);    while(1)    {        scanf("%s",ss);        if(ss[0]=='e')            break;        dlx.init(9*9*9,9*9*4);        for(int i=0;i<81;i++)            if(ss[i]!='.')            {                int x=i/9;                int y=i%9;//0~8                //cout<<x<<y<<endl;                dlx.link(i*9+ss[i]-'0',x*9+ss[i]-'0');                dlx.link(i*9+ss[i]-'0',81+y*9+ss[i]-'0');                dlx.link(i*9+ss[i]-'0',162+(sq[x][y]-1)*9+ss[i]-'0');                dlx.link(i*9+ss[i]-'0',243+x*9+y+1);            }            else            {                for(int j=1;j<=9;j++)                {                    int x=i/9;                    int y=i%9;//0~8                    //cout<<x<<y<<endl;                    dlx.link(i*9+j,x*9+j);                    dlx.link(i*9+j,81+y*9+j);                    dlx.link(i*9+j,162+(sq[x][y]-1)*9+j);                    dlx.link(i*9+j,243+x*9+y+1);                }            }        dlx.dance(0);        for(int i=0;i<dlx.ansd;i++)        {            int v=(dlx.ans[i]-1)%9+1;            int p=(dlx.ans[i]-1)/9;            ss[p]=v+'0';        }        printf("%s\n",ss);    }    return 0;}


0 0