平时挺喜欢写小程序的,但是不知道写啥,偶然看到关于数独的新闻,觉得用小程序实现再合适不过了,网上一搜,有原理,有程序,但是没有找到优秀的代码,干脆自己写了一个,虽然也不优秀,起码自己看得懂。

原理:

1、每个格子可以是1-9的数,n行m列的数确定后,则第n行,第m列,nm所在的”宫“里其他的格子就不能是这个数了。

2、刚开始每个格子都有9个”候选数“,逐步把初始数据添加到最终的格子中,并把相应的格子中的候选数更新,如1所说。

3、开始计算,选出候选数最少的格子,对这些数迭代测试,如把候选数中的第1个添加到最终的格子中。和2一样更新其他格子的候选数列表。

4、3之后自然还是3,所以把3实现为递归要简单一些。而且考虑到本题实际情况最多递归81层,不会栈溢出。而且递归本身还保存了后续所需数据,简化了操作。

代码注释:

g_currentIndex:格子中已经确定结果的个数(添加到格子中的数的个数,递归的深度),索引表示的,所以0代表已添加1个。

g_affectedFlags:位标志,某次添加数据是否对格子产生影响,回退操作要使用。

g_candidate:每个格子的候选列表,用bitset的索引表示。第n位为1,代表n是候选数。

g_candidateNum:每个格子的候选个数,为什么不用bitset的count呢?因为速度太慢。

结果截图:

源码:

#include<iostream>
#include<set>
#include<bitset>
#include<cstring>
#include<time.h>

using namespace std;

int g_currentIndex=-1;
bitset<81> g_affectedFlags[9][9];
bitset<10> g_candidate[9][9];
int g_candidateNum[9][9];
int resultNum;
const int maxNum=5;

int g_map[9][9]={
{8,0,0,0,0,0,0,0,0},
{0,0,3,6,0,0,0,0,0},
{0,7,0,0,9,0,2,0,0},
{0,5,0,0,0,7,0,0,0},
{0,0,0,0,4,5,7,0,0},
{0,0,0,1,0,0,0,3,0},
{0,0,1,0,0,0,0,6,8},
{0,0,8,5,0,0,0,1,0},
{0,9,0,0,0,0,4,0,0},
};

void AddElement(int row,int column,int num)
{
    ++g_currentIndex;
    g_map[row][column]=num;
    
    int old;
    for(int i=0;i<9;++i)
    {
        if(g_map[row][i]==0 && g_candidate[row][i].test(num))
        {
            g_candidate[row][i].reset(num);
            --g_candidateNum[row][i];
            g_affectedFlags[row][i].set(g_currentIndex);
        }
            
        if(g_map[i][column]==0 && g_candidate[i][column].test(num))
        {
            g_candidate[i][column].reset(num);
            --g_candidateNum[i][column];
            g_affectedFlags[i][column].set(g_currentIndex);
        }
    }
    
    int palaceRow=row>2?(row>5?6:3):0;
    int palaceColumn=column>2?(column>5?6:3):0;
    
    for(int i=0;i<3;++i)
    {
        for(int j=0;j<3;++j)
        {
            row=palaceRow+i;
            column=palaceColumn+j;
            if(g_map[row][column]==0 && g_candidate[row][column].test(num))
            {
            g_candidate[row][column].reset(num);
            --g_candidateNum[row][column];
            g_affectedFlags[row][column].set(g_currentIndex);
            }
        }
    }
}

void RecoverElement(int row,int column,int num)
{
    g_map[row][column]=0;
    for(int i=0;i<9;++i)
    {
        if(g_map[row][i]==0 && g_affectedFlags[row][i].test(g_currentIndex))
        {
            g_candidate[row][i].set(num);
            ++g_candidateNum[row][i];
            g_affectedFlags[row][i].reset(g_currentIndex);
        }
        
        if(g_map[i][column]==0 && g_affectedFlags[i][column].test(g_currentIndex))
        {
            g_candidate[i][column].set(num);
            ++g_candidateNum[i][column];
            g_affectedFlags[i][column].reset(g_currentIndex);
        }            
    }
    
    int palaceRow=row>2?(row>5?6:3):0;
    int palaceColumn=column>2?(column>5?6:3):0;
    
    for(int i=0;i<3;++i)
    {
        for(int j=0;j<3;++j)
        {
            row=palaceRow+i;
            column=palaceColumn+j;
            if(g_map[row][column]==0 && g_affectedFlags[row][column].test(g_currentIndex))
            {
            g_candidate[row][column].set(num);
            ++g_candidateNum[row][column];
            g_affectedFlags[row][column].reset(g_currentIndex);
            }    
        }
    }
        
    --g_currentIndex;    
}

void Init()
{
    for(int i=0;i<9;++i)
    {
        for(int j=0;j<9;++j)
        {
            g_candidate[i][j].set();
            g_candidateNum[i][j]=10;
        }
    }

    for(int i=0;i<9;++i)
    {
        for(int j=0;j<9;++j)
        {
            if(g_map[i][j]!=0)
                AddElement(i,j,g_map[i][j]);
        }
    }
}

bool FindBest(int &row,int &column)
{
    int min=999;
    for(int i=0;i<9;++i)
    {
        for(int j=0;j<9;++j)
        {
            if(g_map[i][j]==0 && g_candidateNum[i][j]>1 && g_candidateNum[i][j]<min)
            {
                row=i;
                column=j;
                min=g_candidateNum[i][j];
            }            
        }
    }
    
    if(min==999)
        return false;
    return true;
}

bool CheckResult()
{
    set<int> elements;
    set<int> elements2;
    
    for(int i=0;i<9;++i)
    {
        for(int j=0;j<9;++j)
        {
            elements.insert(g_map[i][j]);
            elements2.insert(g_map[j][i]);
        }
        if(elements.size()!=9)
            return false;
        if(elements2.size()!=9)
            return false;
        elements.clear();
        elements2.clear();
    }
    
    elements.clear();
    int row,column;
    for(int i=0;i<3;++i)
    {
        for(int j=0;j<3;++j)
        {
            row=i*3;
            column=j*3;
            for(int k=0;k<9;++k)
            {
                elements.insert(g_map[row+k/3][column+k%3]);
            }
            if(elements.size()!=9)
                return false;
                
            elements.clear();
        }
    }
    return true;    
}

void OutputResult()
{
    cout<<endl;
    for(int i=0;i<9;++i)
    {
        for(int j=0;j<9;++j)
        {
            cout<<g_map[i][j]<<" ";
        }
        cout<<endl;
    }
}

bool Try()
{
    int row,column;
    if(!FindBest(row,column))
        return true;

    for(int i=1;i<10;++i)
    {
        if(!g_candidate[row][column].test(i))
            continue;
    
        AddElement(row,column,i);
        
        if(Try())
        {
            if(g_currentIndex==80 && CheckResult())
            {
                cout<<endl<<"Result:"<<++resultNum<<endl;
                OutputResult();
                
                if(resultNum>=maxNum)
                    return false;
            
            }
        }
        else
            return false;
        
        RecoverElement(row,column,i);    
    }
    
    return true;
}

int main()
{    
    double start,end,cost;
    start=clock();
    
    Init();
    Try();
    
    if(resultNum)
        cout<<endl<<"OK!"<<endl;
    else
        cout<<endl<<"Wrong Input!"<<endl;
    
    end=clock();
    cost=end-start;
    cout<<"Costed time:"<<cost<<"ms"<<endl;
    
    char c;
    cin>>c;
    
    return 0;
}

其他:

个人觉得这种方法已经到了速度的极限,高手的代码竟然能够达到几十毫秒,表示自叹不如!

我用的例子是”史上最难数独“,确实只有一个解,代码中maxNum可以限制显示结果数量。

附其他高手的截图一张: