递归暴力破解法解数独问题

本文介绍了一个使用C语言编写的数独求解程序。该程序能够在Linux环境下编译运行,并通过读取数据文件sudoku.dat来解析数独题目,最终输出数独的解答。程序采用了位操作和递归回溯算法,有效地解决了数独问题。

C语言源代码如下,在Linux平台下用GCC编译通过并运行。附数据文件sudoku.dat及计算结果。

8--------
--36-----
-7--9-2--
-5---7---
----457--
---1---3-
--1----68
--85---1-
-9----4--

812753649
943682175
675491283
154237896
369845721
287169534
521974368
438526917
796318452

编译及运行方法:

gcc sudoku.c -o sudoku

./sudoku < sudoku.dat

#include <stdio.h>
#include <string.h>

#define ROWS    9
#define COLS    9
#define BLKS    9

#define NAN     '-'

typedef enum { false = 0, true = !false } bool;

struct step {
    char num[ROWS][COLS];
    int cell[ROWS][COLS];
};

static int ctz(int x) { return __builtin_ctz(x); }

static int rowcol2blk(int row, int col) { return row / 3 * 3 + col / 3; }
static int blk2row(int blk) { return blk / 3 * 3; }
static int blk2col(int blk) { return blk % 3 * 3; }

bool setnum(struct step *curr, int row, int col, int num);

void init(struct step *curr)
{
    int row, col;

    for (row = 0; row < ROWS; ++row) {
        for (col = 0; col < COLS; ++col) {
            curr->cell[row][col] = 0x3FE;
            curr->num[row][col] = NAN;
        }
    }
}

bool exclude(struct step *curr, int row, int col, int num)
{
    int x = curr->cell[row][col] & ~(1 << num);

    if (0 == x) return false;

    curr->cell[row][col] = x;
    if (NAN != curr->num[row][col] || 0 != (x & (x - 1)))
        return true;

    return setnum(curr, row, col, ctz(x));
}

bool setnum(struct step *curr, int row, int col, int num)
{
    int mask = 1 << num;
    int blk = rowcol2blk(row, col);
    int r, c, r0, c0;

    if (0 == (curr->cell[row][col] & mask))
        return false;

    curr->num[row][col] = '0' + num;
    curr->cell[row][col] = mask;

    for (r = 0; r < ROWS; ++r) {
        if (r == row) continue;
        if (!exclude(curr, r, col, num))
            return false;
    }

    for (c = 0; c < COLS; ++col) {
        if (c == col) continue;
        if (!exclude(curr, row, c, num))
            return false;
    }

    r0 = blk2row(blk);
    c0 = blk2col(blk);
    for (r = r0; r < r0 + 3; ++r) {
        for (c= c0; c < c0 + 3; ++c) {
            if (r == row && c == col) continue;
            if (!exclude(curr, r, c, num))
                return false;
        }
    }

    return true;
}

bool try(struct step *curr)
{
    struct step next;
    int row, col, num;
    int cell;

    for (row = 0; row < ROWS; ++row) {
        for (col = 0; col < COLS; ++col) {
            if (NAN == curr->num[row][col])
                goto outer;
        }
    }

outer:
    if (row > ROWS) return true;

    cell = curr->cell[row][col];
    while (0 != cell) {
        memcpy(&next, curr, sizeof next);

        num = ctz(cell);
        if (setnum(&next, row, col, num) && try(&next)) {
            memcpy(curr, &next, sizeof *curr);
            break;
        }

        cell &= ~(1 << num);
    }

    return (0 != cell);
}

int main()
{
    struct step step;
    int row, col, num;

    init(&step);

    for (row = 0; row < ROWS; ++row) {
        for (col = 0; col < COLS; ++col) {
            if (NAN == (num = getchar())) continue;
            if (!setnum(&step, row, col, num - '0')) {
                puts("no answer\n");
                return 1;
            }
        }
        num = getchar(); /* new line */
    }

    if (!try(&step)) {
        puts("no answer\n");
        return 1;
    }

    for (row = 0; row < ROWS; ++row) {
        for (col = 0; col < COLS; ++col) {
            putchar(step.num[row][col];
        }
        putchar('\n');
    }

    return 0;
}


 

<think>嗯,用户想了解算题中的暴力解的概念和应用,以及如何实现。首先,我需要明确暴力解的定义。暴力解通常是指通过穷举所有可能的解来找到正确答案的方,不考虑时间或空间复杂度,只追求正确性。那用户可能刚接触算,需要基础的解释,所以得用简单的中文说明。 接下来,用户可能想知道暴力解的应用场景。比如,在哪些常见的问题中会用到暴力解?比如排列组合问题、搜索问题,或者像两数之和这样的题目。这时候需要举几个例子,比如全排列、01背包问题,或者像用户提供的引用中的例子,比如两个数之间有整除关系的情况,确定一个数然后筛选其他数。这样用户更容易理解。 然后,实现暴力解的方。可能需要分步骤说明,比如明确问题、确定解空间、遍历所有可能、筛选有效解。这部分需要结合具体的例子,比如全排列问题如何通过递归生成所有可能的排列,再比如01背包问题如何生成所有物品的子集并计算价值。这样用户可以看到具体的实现步骤。 用户提供的引用中有几个例子,比如引用1提到暴力解不是一味的暴力,而是利用某些条件来减少计算量。比如两数之间整除,只需要确定一个数,另一个数可以通过整除关系得到,然后筛选。这说明暴力解也可以有一定的优化,但本质上还是穷举。引用3提到01背包的暴力解是生成所有物品的选与不选的状态,然后计算最大价值。这需要解释如何生成所有可能的组合,比如用递归或位运算。 用户可能还想知道暴力解的优缺点,比如虽然简单但效率低,适用于小规模数据。或者在某些情况下,暴力解可以作为基准测试,验证更优算的正确性。这部分需要提到时间复杂度和适用场景,比如在数据量大的情况下不适合使用暴力解。 另外,用户可能对如何在实际编程中实现暴力解感兴趣,比如递归、循环嵌套、回溯等方。需要给出代码示例,比如全排列的递归实现,或者01背包的暴力解代码。但根据用户提供的引用,引用2是C语言的暴力解示例,引用3是Java的01背包暴力解,可能可以提到这些例子,但不需要具体代码,因为用户的问题是关于概念和实现方。 最后,生成相关问题,比如暴力解的适用场景、优化方、与其他算的比较等。这部分需要根据用户提供的示例中的相关问题,但可能用户更关心如何判断何时使用暴力解,或者如何改进暴力解。 总结回答的结构:定义暴力解,特点,应用场景,实现步骤,优缺点,示例说明,以及相关问题。需要注意使用中文,行内公式用$...$,独立公式用$$...$$,并正确引用提供的参考资料。</think>### 暴力解的概念与实现 #### 1. 核心定义 暴力解(Brute Force)**指通过穷举所有可能的候选解来寻找问题答案的算策略**,其特点为: - **不考虑时间复杂度优化**,以正确性为唯一目标 - **解空间遍历**是核心操作(例如排列组合、子集枚举) - **代码逻辑简单**,适合作为解题起点或验证基准 #### 2. 典型应用场景 | 问题类型 | 实现方式 | 时间复杂度 | 示例问题 | |-------------------|---------------------------|------------------|-----------------------------| | 排列组合问题 | 递归回溯/多层循环 | $O(n!)$ | 全排列[^3] | | 子集问题 | 二进制位掩码 | $O(2^n)$ | 01背包暴力解[^3] | | 数值计算问题 | 遍历所有可能性筛选 | 取决于约束条件 | 整除关系筛选[^1] | | 搜索问题 | 线性/网格遍历 | $O(n^2)$或更高 | 矩阵元素查找 | #### 3. 实现步骤分解 1. **明确问题边界**:确定输入范围与约束条件 (例如:数组长度≤20时适用暴力解) 2. **构建解空间**:用数学方描述所有可能解 $$S = \{s_1,s_2,...,s_n\} \text{ 满足 } f(s_i)=true$$ 3. **遍历机制选择**: - 递归回溯(树形结构问题) - 多重循环(固定层数问题) - 位运算(子集问题) 4. **剪枝优化**(可选): 在遍历时提前排除无效分支(如引用[1]中利用整除关系减少计算) #### 4. 代码实现示例 以**两数整除问题**为例(引用[1]): ```python def find_pairs(nums): result = [] for i in range(len(nums)): for j in range(len(nums)): if i != j and nums[i] % nums[j] == 0: result.append((nums[i], nums[j])) return result ``` 该实现通过双重循环遍历所有数对组合,时间复杂度为$O(n^2)$。 #### 5. 优缺点分析 | 优点 | 缺点 | |---------------------------|---------------------------| | ① 逻辑简单易实现 | ① 时间复杂度指数级增长 | | ② 保证找到正确解(若有) | ② 无处理大规模数据 | | ③ 适合验证算正确性 | ③ 空间消耗可能过大 | #### 6. 优化方向建议 - **剪枝策略**:在遍历过程中提前终止无效分支 - **记忆化存储**:缓存重复计算结果(如动态规划) - **问题转化**:将原问题转化为数学公式简化计算
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值