很早之前写的一个数独求解程序,觉得还可以
使用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;
}