使用双向十字链表(或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

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

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

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

时间: 2024-08-29 01:08:44

使用双向十字链表(或Dancing Links)解数独游戏的相关文章

HDU 1426 dancing links解决数独问题

题目大意: 这是一个最简单的数独填充题目,题目保证只能产生一种数独,所以这里的初始9宫格较为稠密,可以直接dfs也没有问题 但最近练习dancing links,这类数据结构解决数独无疑效率会高很多 dancing links的数独限制条件是: 1.每行有9个元素,共9行 对应dlx81列 2.每列有9个元素,共9行 对应dlx81列 3.每个九宫格有9个元素,共9行 对应dlx81列 4.81个格子,每个格子最多有一个数 1 #include <iostream> 2 #include &l

LeetCode----Sudoku Solver+精确覆盖问题解法(Dancing Links)

BackGround: 做完LeetCode上的数独题目好长时间了,今天将做题时参考的Algorithm X 以及 Dancing Links 整理出来.话说理解算法+写出程序一共用了三天,智商果然余额不足... 介绍: 由于Dancing Links 是为了解决数独问题学习的,那就从数独问题下手,围绕数独问题展开对Algorithm X 和 Dancing Links的介绍,最后将数独问题的解法以及Java源码放出来. 精确覆盖问题: 没错,解数独的问题就是一个解  精确覆盖   问题的过程,

ZOJ 3209 Dancing Links

思路:这题挺好的,本来模板不是自己敲的嘛,理解了Dancing Links后是找了一个模板的,然后正好这题让自己加深理解了,也知道在实际中怎么建矩阵求解了. 把n*m的矩阵看成n*m个格子,像那个数独一样,作为n*m列:每一个矩形一行. 行列都建好矩阵后,就可以用舞蹈链求解了. 问题即转化为从这些行中选择最少的一部分使每一列被覆盖且仅覆盖一次. #pragma comment(linker, "/STACK:1024000000,1024000000") #include<ios

POJ 3740 Dancing Links

Dancing Links学习:http://www.cnblogs.com/steady/archive/2011/03/15/1984791.html 以及图文学习:http://www.cnblogs.com/grenet/p/3145800.html 思路:这题是Dancing Links即DLX的最简单题目了吧,看懂了这个知识点之后.也不想自己敲了.然后搜索了好多个代码模板.认为这个我比較好理解也比較好掌握.然后就用这个模板了. #include<iostream> #include

Dancing Links 学习 AND 代码详解

今天花时间学习了下Dancing Links,其核心思想是降低在搜索中的范围,减少复杂.降低的方法就是将用链式结构构造的图中不需要的点去掉.如果回溯再恢复. 这个方法依赖的数据结构是用数组存储的十字链表L[NN],R[NN],U[NN],D[NN] 左右上下的链接 构造数据结构: head,cnt,L[NN],R[NN],U[NN],D[NN],H[NN],COL[NN],S[NN],ROW[NN] head就是头结点,cnt就是在构图时结点的编号,S[NN]是某一列上有多少个元素,COL[NN

浅入 dancing links x(舞蹈链算法)

abastract:利用dancing links 解决精确覆盖问题,例如数独,n皇后问题. 要学习dacning links 算法,首先要先了解该算法所适用的问题,即精确覆盖问题,下面先了解精确覆盖问题. 精确覆盖问题 何为精确覆盖问题 在一个全集X中若干子集的集合为S,精确覆盖(Exactcover)是指,S的子集S*,满足X中的每一个元素在S*中恰好出现一次. 定义 满足以下条件的集合为一个精确覆盖:  S*中任意两个集合没有交集,即X中的元素在S*中出现最多一次  S*中集合的全集为X,

跳跃的舞者,舞蹈链(Dancing Links)算法——求解精确覆盖问题(转)

跳跃的舞者,舞蹈链(Dancing Links)算法——求解精确覆盖问题 转:http://www.cnblogs.com/grenet/p/3145800.html 精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1 例如:如下的矩阵 就包含了这样一个集合(第1.4.5行) 如何利用给定的矩阵求出相应的行的集合呢?我们采用回溯法 矩阵1: 先假定选择第1行,如下所示: 如上图中所示,红色的那行是选中的一行,这一行中有3个1,分别是第3.5.

HUST 1017 - Exact cover (Dancing Links 模板题)

1017 - Exact cover 时间限制:15秒 内存限制:128兆 自定评测 5584 次提交 2975 次通过 题目描述 There is an N*M matrix with only 0s and 1s, (1 <= N,M <= 1000). An exact cover is a selection of rows such that every column has a 1 in exactly one of the selected rows. Try to find o

poj 3074 Sudoku(Dancing Links)

Sudoku Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8152   Accepted: 2862 Description In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For example, . 2 7 3 8 . . 1 . . 1 . . . 6 7 3 5 . . .