首页 > 代码库 > 使用双向十字链表(或Dancing Links)解数独游戏

使用双向十字链表(或Dancing Links)解数独游戏

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct Data
{
    void assign(int x,int y,int z)
    {
        row=x;
        col=y;
        val=z;
    }
    int row,col,val;
} data[730];
struct Node
{
    Node(int x=0,int y=0):
        row(x),col(y),up(this),down(this),left(this),right(this) {}
    int row,col;
    Node *up,*down,*left,*right;
}**rowHead,**colHead;
char tempData[9][10];
int rowCounter[730],colCounter[325];
int ans[10][10];
int belong[10][10],belong1[10][10],belong2[10][10],belong3[10][10],belong4[10][10];
int belongX[325],belongY[325];
void init()
{
    rowHead=new Node*[730],colHead=new Node*[325];
    for(int i=0; i<=729; i++)rowHead[i]=new Node(i,0);
    for(int i=0; i<=324; i++)colHead[i]=new Node(0,i);
    for(int i=0; i<=729; i++)
        if(i&&i!=729)
        {
            rowHead[i]->up=rowHead[i-1];
            rowHead[i]->down=rowHead[i+1];
        }
        else if(!i)
        {
            rowHead[i]->up=rowHead[729];
            rowHead[i]->down=rowHead[i+1];
        }
        else
        {
            rowHead[i]->up=rowHead[i-1];
            rowHead[i]->down=rowHead[0];
        }
    for(int i=0; i<=324; i++)
        if(i&&i!=324)
        {
            colHead[i]->left=colHead[i-1];
            colHead[i]->right=colHead[i+1];
        }
        else if(!i)
        {
            colHead[i]->left=colHead[324];
            colHead[i]->right=colHead[i+1];
        }
        else
        {
            colHead[i]->left=colHead[i-1];
            colHead[i]->right=colHead[0];
        }
    memset(rowCounter,0,sizeof(rowCounter));
    memset(colCounter,0,sizeof(colCounter));
    memset(ans,0,sizeof(ans));
}
void link(int x,int y)
{
    Node *newNode=new Node(x,y);
    if(rowHead[x]->right==rowHead[x])
    {
        newNode->left=newNode->right=rowHead[x];
        rowHead[x]->left=rowHead[x]->right=newNode;
        rowCounter[x]++;
    }
    else
    {
        for(Node *i=rowHead[x]->right;; i=i->right)
            if(newNode->col<i->col||!i->col)
            {
                newNode->left=i->left;
                newNode->right=i;
                i->left->right=newNode;
                i->left=newNode;
                rowCounter[x]++;
                break;
            }
    }
    if(colHead[y]->down==colHead[y])
    {
        newNode->up=newNode->down=colHead[y];
        colHead[y]->up=colHead[y]->down=newNode;
        colCounter[y]++;
    }
    else
    {
        for(Node *i=colHead[y]->down;; i=i->down)
            if(newNode->row<i->row||!i->row)
            {
                newNode->up=i->up;
                newNode->down=i;
                i->up->down=newNode;
                i->up=newNode;
                colCounter[y]++;
                break;
            }
    }
}
void remove(Node *&x)
{
    x->left->right=x->right,x->right->left=x->left;
    for(Node *i=x->down; i!=x; i=i->down)
        for(Node *j=i->right; j!=i; j=j->right)
            if(j!=rowHead[i->row])
            {
                j->up->down=j->down;
                j->down->up=j->up;
                colCounter[j->col]--;
            }
}
void resume(Node *&x)
{
    for(Node *i=x->up; i!=x; i=i->up)
        for(Node *j=i->left; j!=i; j=j->left)
            if(j!=rowHead[i->row])
            {
                colCounter[j->col]++;
                j->down->up=j;
                j->up->down=j;
            }
    x->right->left=x,x->left->right=x;
}
bool dance()
{
    if(colHead[0]->right==colHead[0])return true;
    int best=2147483647;
    Node *bestNode;
    for(Node *i=colHead[0]->right; i!=colHead[0]; i=i->right)
        if(best>colCounter[i->col])
        {
            best=colCounter[i->col];
            bestNode=i;
        }
    remove(bestNode);
    for(Node *i=bestNode->down; i!=bestNode; i=i->down)
    {
        ans[data[i->row].row][data[i->row].col]=data[i->row].val;
        for(Node *j=i->right;j!=i;j=j->right)
            if(j!=rowHead[i->row])
                remove(colHead[j->col]);
        if(dance())return true;
        for(Node *j=i->left;j!=i;j=j->left)
            if(j!=rowHead[i->row])
                resume(colHead[j->col]);
    }
    resume(bestNode);
    return false;
}
int main()
{
    for(int i=1; i<=9; i++)
        for(int j=1; j<=9; j++)
            belong[i][j]=3*((i-1)/3)+(j-1)/3+1,
                         belong1[i][j]=9*(i-1)+j,
                                       belong2[i][j]=9*(i+8)+j,
                                               belong3[i][j]=9*(i+17)+j,
                                                       belong4[i][j]=9*(i+26)+j;
    for(int i=1; i<=81; i++)
        belongX[i]=(i-1)/9+1,
                   belongY[i]=(i-1)%9+1;
    for(int i=82; i<=162; i++)
        belongX[i]=(i-82)/9+1,
                   belongY[i]=(i-82)%9+1;
    for(int i=163; i<=243; i++)
        belongX[i]=(i-163)/9+1,
                   belongY[i]=(i-163)%9+1;
    for(int i=244; i<=324; i++)
        belongX[i]=(i-244)/9+1,
                   belongY[i]=(i-244)%9+1;
    int T;
    scanf("%d",&T);
    getchar();
    for(int countT=1; countT<=T; countT++)
    {
        init();
        int countRow=0;
        for(int i=0; i<9; i++)
        {
            gets(tempData[i]);
            for(int j=0; j<9; j++)
                if(tempData[i][j]!='0')
                {
                    int tempNumber=tempData[i][j]-'0';
                    countRow++;
                    data[countRow].assign(i+1,j+1,tempNumber);
                    link(countRow,belong1[i+1][j+1]);
                    link(countRow,belong2[i+1][tempNumber]);
                    link(countRow,belong3[j+1][tempNumber]);
                    link(countRow,belong4[belong[i+1][j+1]][tempNumber]);
                }
                else
                {
                    int counter=9,tempNumber=1;
                    while(counter--)
                    {
                        countRow++;
                        data[countRow].assign(i+1,j+1,tempNumber);
                        link(countRow,belong1[i+1][j+1]);
                        link(countRow,belong2[i+1][tempNumber]);
                        link(countRow,belong3[j+1][tempNumber]);
                        link(countRow,belong4[belong[i+1][j+1]][tempNumber]);
                        tempNumber++;
                    }
                }
        }
        if(dance())
        {
            for(int i=1; i<=9; i++)
            {
                for(int j=1; j<=9; j++)printf("%d",ans[i][j]);
                putchar('\n');
            }
        }
    }
    return 0;
}


之前水平不够,写了一个不加任何优化,直接搜的解数独程序。

现在水平提高,学会如何用双向十字链表优化时间复杂度来解数独游戏,还不能直接使用它,必须将它转化成“精确覆盖模型”。

为什么?

我尝试着写了一个单纯用双向十字链表来解数独游戏的程序,它有优点也有缺点。之前暴搜的那个程序解特别稀疏的数独就会超时,比如:全是空儿的数独。但是单纯用双向十字链表的程序能够解决这样“全是空儿”的数独,但是还不够~对于有些数独,尤其是唯一解的数独,也同样会超时的,比如下面这个很难很难的数独:

800 000 000

003 600 000

070 090 200

050 007 000

000 045 700

000 100 030

001 000 068

008 500 010

090 000 400

上面的数独对于单纯使用双向十字链表的程序就是克星,因为它的解用一种哲学上的思维来说就是:这个问题的解被排在了后面。对的,深度优先搜索都是一直搜到底的,对于有些问题解被“排在后面”的,深度优先搜索就不行了。

到底什么地方还能优化呢?

其实我们在单纯使用双向十字链表而不进行模型转化的时候,我们与以往搜索唯一的不同就在于我们能够直接找到哪些有空儿、哪些没有空儿,但是有一点:我们还是从1到9枚举了每一个数字,然后对于每个数字通过之前打上的标记判断能不能填,虽然只有9个数字,但是这9个数字的枚举是放在递归层次里的,非常费时。

现在我们不仅需要能够直接知道哪些空儿可以填,我们还需要直接知道哪些数字是可以填的,这样可以加快进程~~~

怎样转化成精确覆盖模型?


构造这样的矩阵:


对于第1~81列:

第1列:在(1,1)填了数字

第2列:在(1,2)填了数字

。。。。。。

第n列:。。。。。。

映射:row=(n-1)/9+1,col=(n-1)%9+1,n=9*(row-1)+col


对于第81~162列:

第1列:在第1行填了数字1

第2列:在第2行填了数字2

。。。。。。

第n列:。。。。。。

映射:row=(n-82)/9+1,num=(n-82)%9+1,n=9*(row+8)+num


对于第163~243列:

第1列:在第1列填了数字1

第2列:在第2列填了数字2

。。。。。。

第n列:。。。。。。

映射:col=(n-163)/9+1,col=(n-163)%9+1,n=9*(col+17)+num


对于第244~324列:

第1列:在第1宫填了数字1

第2列:在第1宫填了数字2

。。。。。。

第n列:。。。。。。

映射:grid=(n-244)/9+1,num=(n-244)%9+1,n=9*(grid+26)+num


对于一个给出的矩阵,如果在(x,y)处是3,那么就插入这样一行

它的9*(x-1)+y,9*(x+8)+3,9*(y+17)+3,9*(grid(x,y),26)+num(坐标映射到宫:3*((x-1)/3)+(y-1)/3+1)

以上四列都是1,但是插入行的时候要记录对应行所代表的意义是什么,这样在搜索时就能很轻易得到信息。


如果是空儿,那么就按照上面的方法在对应坐标将1~9依次插入,总共就是4*9=36个节点。


接下来直接解精确覆盖就可以了,通过选的行的编号,可以知道在那个位置填入几,当然这也要预处理一下,我用的方法就是直接将信息放在Data结构体中,当然可能也有公式,我就不推了。


写的有些潦草,不如网上一些大牛写的好。

下面是一个测试样例和结果:

1
800000000
003600000
070090200
050007000
000045700
000100030
001000068
008500010
090000400


812753649
943682175
675491283
154237896
369845721
287169534
521974368
438526917
796318452

时间相当快,可见双向十字链表+精确覆盖模型的解题速度之快。

哭不过感觉本文还是写挫了,凑合看吧。