回溯法解数独

前天在我的小pad上装了个数独游戏,完了几把后兴趣索然了。不过突然想起来一直想写个解数独的程序,不过因为懒和拖拉,就一直没写。今天花了30分钟写了个解数独的程序,贴代码:

#include <cstdio>
#include <string>
#include <vector>
//某个数字填入后,需要检查的index
void get_affected_index(const int has_filled_index, int * affected_index) {
    int row_number = has_filled_index / 9;
    int column_number = has_filled_index % 9;
    int affect_index_idx = 0;
    for(int i = 0; i < 9 ; ++i){
        if(i != row_number)
            affected_index[affect_index_idx++] = i*9 + column_number;
    }
    for(int i = 0; i < 9; ++i){
        if(i != column_number)
            affected_index[affect_index_idx++] = row_number * 9 + i;
    }
    int vertical_index = row_number % 3;
    int horizonal_index = column_number % 3;
    int first_row =0, second_row = 0;
    int first_column=0, second_column =0;
    if (vertical_index == 0){
        first_row = row_number + 1;
        second_row = row_number + 2;
    } else if (vertical_index == 1) {
        first_row = row_number - 1;
        second_row = row_number + 1;
    } else if (vertical_index == 2) {
        first_row = row_number - 2;
        second_row = row_number - 1;
    }
    if(horizonal_index == 0) {
        first_column = column_number + 1;
        second_column = column_number + 2;
    } else if (horizonal_index == 1) {
        first_column = column_number - 1;
        second_column = column_number + 1;
    } else if (horizonal_index == 2) {
        first_column = column_number - 2;
        second_column = column_number - 1;
    }
    affected_index[affect_index_idx++] = first_row * 9 + first_column;
    affected_index[affect_index_idx++] = first_row * 9 + second_column;
    affected_index[affect_index_idx++] = second_row * 9 + first_column;
    affected_index[affect_index_idx++] = second_row * 9 + second_column;
}
//填入一个数字后检查是否能够满足数独需求
bool check_status(char * shudu, const int has_filled_index) {
    int affected_index[20] = {0};
    get_affected_index(has_filled_index, affected_index);
    for (int i=0; i<20; i++) {
        int current_index = affected_index[i];
        if(shudu[current_index] == shudu[has_filled_index])
            return false;
    }
    return true;
}
//回溯法求解数独
bool solve(char * shudu, const int * blank_index, const int blank_num) {
    int current_blank_num = 0;
    while(current_blank_num < blank_num) {
        if(current_blank_num<0){
            return false;
        }
        int current_blank_index = blank_index[current_blank_num];
        shudu[current_blank_index]++;
        if (shudu[current_blank_index] == '9'+1){    //回溯:最后一个填入的数字清零并回退一格
            shudu[current_blank_index] = '0';
            current_blank_num--;
        }
        if (check_status(shudu, current_blank_index)) {    //刚填的数字合法
            current_blank_num ++;
            continue;
        } else { //刚填的数字不合法
            continue;
        }
    }
    return true;
}
//数独输出
void print_shudu(const char * shudu) {
    for(int i=0; i<9; i++) {
        for(int j=0; j<9; j++)
            printf("%c ", shudu[ i*9 + j ]);
        printf("\n");
    }
}
//初始化数独:读取输入并计算多少个空位,纪录空位的index
void init_blank_index(const char * shudu, int * blank_index, int * blank_num){
    int index = 0;
    for(int i=0; i<81; i++) {
        if(shudu[i] == '0'){
            blank_index[index++] = i;
        }
    }
    *blank_num = index;
    return;
}
int main(int argc, char ** argv) {
    char shudu[81] = {0};
    int blank_index[81] = {0};
    int blank_num = 0;
    printf("Please Input the shudu sequence, no blank character needed:\n");
    scanf("%s",shudu);    //不做任何合法性检测
    init_blank_index(shudu, blank_index, &blank_num);
    if(solve(shudu, blank_index, blank_num)){
        print_shudu(shudu);
    }
    return 0;
}

代码居然写了116行,比较扯。大部分代码是输入输出,检查填入数字合法性的部分写的太拖沓了。

往深了想一下,这个算法可以在哪儿进行优化呢?一个启发式的想法是:在初始化的时候就检查每个位置可能出现的数字,把可能出现的数字较少的位置放在前面先填。这也是我们玩数独的时候经常使用的策略——如果一个位置只可能有一个数字出现,那么就先把他填上,作为一个已知的条件,然后再继续寻找下一个……直到找不到这样的位置了,再进行试探。这样的话,外层循环的次数就大量减少,省去了很多无用的试探。

尽管这个想法或许真的能降低时间复杂度的,但缺少数学证明。不知道哪位大牛能够给出这个时间复杂度的证明?


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值