c语言数独求解dfs

很早之前写的一个数独求解程序,觉得还可以

使用0代替空方块

258000003
700520000
000063027
009007300
000081002
800000710
084090005
060702040
020410600

运行结果:
在这里插入图片描述
大致说一下思路吧,按行遍历board,对于99的board上的每一个位置都维护的有一个left数组,该数组表示该位子可以放置的数字,同时该数组会在不断dfs中动态改变。对于数独程序来说主要有3个考虑点:行、列、所对应的33方块我所采用的标记方法并不是置1和置0,而是加1和减1,之所以如此是因为要考虑“影响”的叠加,考虑上图0,0位置的2和1,4位置的2。1,4位置的2和0,0位置的2的重复区域为700,也就是求解之后为736的位置,如果dfs回溯撤销了后面一个2的“影响”通过减1,700位置上2的标记可能仍然不为0,说明还有其它位子的2"影响着"…

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>  
#include <ctype.h>
#include <stdlib.h>  
#include <io.h>
#include <time.h>
#include <string.h>

#define SIZE 9

struct {
    unsigned int left[10];  //剩余可以设置的数字, left[i] = 0 表示i可用
    unsigned short num;   //该位置上的数字
    unsigned short set;  //改位置设置的数字,只有输入的时候该位置上没有数字才会使用到 
}board[SIZE][SIZE];

const char *status_str[] = {"fail", "success"};

void Print() {//打印该数独,红色为所填数字
    for (int i = 0; i < SIZE; i++) {
        if (i % 3 == 0) {
            for (int j = 0; j < SIZE * 2 + 4; j++)
                putchar('-');
            putchar('\n');
        }

        for (int j = 0; j < SIZE; j++) {
            if (j % 3 == 0)
                putchar('|');
            if (board[i][j].num == 0)
                printf("\033[0;31m%2d\033[0m", board[i][j].set);
                //printf("%2d", board[i][j].set);
            else
                printf("%2d", board[i][j].num);
        }
        printf("|\n");
    }
    for (int i = 0; i < SIZE * 2 + 4; i++)
        putchar('-');
    putchar('\n');
}


//设置bit位为不可用状态,与unset相反
void set(int i, int j, int bit, int flag) {
    for (int k = 0; k < SIZE; k++) {
        board[k][j].left[bit] += flag; //将第j列bit位设置为不可用  
        board[i][k].left[bit] += flag;  //将第i行bit位设置为不可用
    }

    //将位置为i,j所在的3*3小方块中将bit位设置为不可用
    for (int k = i / 3 * 3; k < (i / 3 + 1) * 3; k++)
        for (int m = j / 3 * 3; m < (j / 3 + 1) * 3; m++)
            board[k][m].left[bit] += flag;
}

void init() {
    for (int i = 0; i < SIZE; i++)
        for (int j = 0; j < SIZE; j++) {
            if (board[i][j].num > 0)
                set(i, j, board[i][j].num, 1);
            else if (board[i][j].set > 0)
                set(i, j, board[i][j].set, 1);
        }
}



int solve(int pos) {
    int i = pos / SIZE;
    int j = pos % SIZE;
    int bit, left;
    //printf("%d %d %d\n", i, j, board[i][j].left);
    if (pos == SIZE * SIZE) return 1; //找到结果,直接返回

    if (board[i][j].num > 0)  //当前位置已有数字,设置下一个位置
        return solve(pos + 1);
    else
        for (left = 1; left < 10; left++) { //遍历所有可设置的数字   
            if (board[i][j].left[left] != 0) continue; //left[i] != 0 说明i不可用 跳过
            set(i, j, left, 1);  //标记bit为不可用
            board[i][j].set = left;  //设置该位置上的数字

            if (solve(pos + 1)) return 1; //找到结果直接返回

            set(i, j, left, -1);  //取消bit位的标记
            board[i][j].set = 0;  //取消该位置上的数字

            //init();
        }
    return 0;//找不到结果    
}

int main() {
    clock_t start, end;

    start = clock();//获取开始时间

    char infile[50];
    char outfile[50];
    int nums = 0;
    int fail = 0;
    int success = 0;
    int status;

    char c;
    for (int i = 0; i < SIZE; i++)
        for (int j = 0; j < SIZE; j++) {
            while ((c = getchar()) && !isdigit(c));
            board[i][j].num = c - '0';
            board[i][j].set = 0;
        }
    Print(); //打印原始数独 
    init();
    status = solve(0);
    if (status){
        Print();
    }  
    else{
        printf("No answer\n"); 
    }   


    end = clock();//获取结束时间

    printf("-----------------%.3lf seconds--------------------\n", ((double)(end - start))/CLOCKS_PER_SEC);

    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值