编程之美之数独求解器的C++实现方法

112 篇文章 0 订阅
81 篇文章 0 订阅

编程之美的第一章的第15节,讲的是构造数独,一开始拿到这个问题的确没有思路, 不过看了书中的介绍之后, 发现原来这个的求解思路和N皇后问题是一致的, 但是不知道为啥,反正一开始确实没有想到这个回溯法,知道是用回溯法求解之后,问题就变得容易了很多。
这里我们不打算实现数独的构造,相反的,我们实现一个数独求解器,以后妈妈再也不用担心我的数独了。

当然求解器的思路和构造数独的思路一样,都是回溯法搜索,这里不再过多说明。

程序运行说明:
1.把待求解的数独数据放到in.txt文件中, 程序会自动读取他,并将解输出到屏幕和out.txt文件中。

2.控制台颜色改变 使用了SetConsoleTextAttribute函数,具体使用细节可以参考 C/C++改变控制台输出字体的背景和颜色(windows)

3.关于程序运行时间统计,使用getTickCount()或者clock()都是可以的

运行效果如下:
这里写图片描述
红色表示部分就是求解出来的值了>_<~~

按照惯例下面是源码:

// ================【shudu.h】===========
#pragma once

#define N 9
#include <fstream>

typedef struct _SearchNode 
{
    int row;
    int col;
    int data;
}SearchNode;

class CShuDu
{
public:
    CShuDu();
    ~CShuDu();

    void CreateSolution();  

private:
    bool isOK(int row, int col);
    void solve(int searchNode_id);
    void writeFile(int id);
    void display(int id);
    void initShudu();
    void output(int id);

private:
    int m_shudu[N + 2][N + 1];
    bool m_find;
    int m_solution_id;
    std::fstream outfile;

    SearchNode m_searchNode[N * N];
    int m_searchLength;
};
// =====================【shudu.cpp】==================
#include <windows.h>

#include "ShuDu.h"
#include <stdio.h>
#include <string.h>
#include <fstream>
#include <iostream>



CShuDu::CShuDu()
{
    memset(m_shudu, 0, sizeof(m_shudu));
    m_find = false;
    m_solution_id = 0;

    outfile.open("out.txt", std::ios::out);
    m_searchLength = 0;
    memset(m_searchNode, 0, sizeof(m_searchNode));
}

CShuDu::~CShuDu()
{
    outfile.close();
}

void CShuDu::CreateSolution()
{
    initShudu();
    solve(1);
}

bool CShuDu::isOK(int row, int col)
{
    bool isOK = true;
    // 列方向检测
    for (int i = 1; i != 10; i++)
    {
        if (i == row)
            continue;

        isOK = (m_shudu[i][col] == m_shudu[row][col]) ? false : true;
        if (isOK == false)
            return isOK;
    }

    // 行检测
    for (int i = 1; i != 10; i++)
    {
        if (i == col)
            continue;

        isOK = (m_shudu[row][i] == m_shudu[row][col]) ? false : true;
        if (isOK == false)
            return isOK;
    }

    // 区域检测
    int block_start_row = (row - 1) / 3 * 3 + 1;
    int block_start_col = (col - 1) / 3 * 3 + 1;
    for (int i = block_start_row; i != block_start_row + 3; i++)
    {
        for (int j = block_start_col; j != block_start_col + 3; j++)
        {
            if (i == row && j == col)
                continue;

            isOK = (m_shudu[i][j] == m_shudu[row][col]) ? false : true;
            if (isOK == false)
                return isOK;
        }
    }

    return isOK;
}

void CShuDu::solve(int searchNode_id)
{
    if (m_find == true)
        return;

    if (m_searchLength + 1 == searchNode_id)
    {
        //m_find = true;
        m_solution_id++;
        output(m_solution_id);
        return;
    }

    for (int i = 1; i != 10; i++)
    {
        int row = m_searchNode[searchNode_id].row;
        int col = m_searchNode[searchNode_id].col;
        m_shudu[row][col] = i;
        if (isOK(row, col))
        {
            solve(searchNode_id + 1);

            row = m_searchNode[searchNode_id + 1].row;
            col = m_searchNode[searchNode_id + 1].col;
            m_shudu[row][col] = 0;
        }           
    }
}

void CShuDu::display(int id)
{
    std::cout << "===========================第" << id << "组数独数据=============================" << std::endl;
    system("title 数独求解器 by zhyh2010 version 1.0");
    int search_id = 1;
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    for (int i = 1; i != 10; i++)
    {
        for (int j = 1; j != 10; j++)
        {
            if (i == m_searchNode[search_id].row
                && j == m_searchNode[search_id].col)
            {
                SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
                search_id++;
            }               
            else
                SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN);
            std::cout << m_shudu[i][j] << " ";
        }
        std::cout << std::endl;
    }
    std::cout << std::endl;
    std::cout << std::endl;
    SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY);
}

void CShuDu::writeFile(int id)
{
    outfile << "===========================第" << id << "组数独数据=============================" << std::endl;
    for (int i = 1; i != 10; i++)
    {
        for (int j = 1; j != 10; j++)
        {
            outfile << m_shudu[i][j] << " ";
        }
        outfile << std::endl;
    }
    outfile << std::endl;
    outfile << std::endl;
}

void CShuDu::output(int id)
{
    display(id);
    writeFile(id);

    //getchar();
}

void CShuDu::initShudu()
{
    std::ifstream infile("in.txt");
    for (int i = 1; i != 10; i++)
    {
        for (int j = 1; j != 10; j++)
        {
            infile >> m_shudu[i][j];
            if (m_shudu[i][j] == 0)
            {
                m_searchLength++;
                m_searchNode[m_searchLength].row = i;
                m_searchNode[m_searchLength].col = j;
            }

        }
    }
}
// =====================【数独求解器 main.cpp】==================
// @ author         :           zhyh2010
// @ date           :           20150624
// @ version        :           1.0
// @ description    :           C++
// =====================【数独求解器】==================

#include <stdio.h>
#include "ShuDu.h"
#include <time.h>

void main()
{
    //DWORD t_start = GetTickCount();
    auto t = clock();

    CShuDu instance;
    instance.CreateSolution();

    //DWORD t_end = GetTickCount();
    printf("程序运行时间为 %d ms\n", 1000*static_cast<float>(clock() - t)/CLOCKS_PER_SEC);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值