|
| 1 | +#include "../utils/common.h" |
| 2 | + |
| 3 | +#define MAX_SIZE 100 |
| 4 | + |
| 5 | +/** |
| 6 | + * backtrack - 回溯算法解决N皇后问题的核心函数 |
| 7 | + * @row: 当前正在处理的行号(从0开始) |
| 8 | + * @n: 棋盘的总行数/列数(即N皇后问题中的N) |
| 9 | + * @state: 当前棋盘状态的二维数组,'Q'表示皇后,'#'表示空 |
| 10 | + * @res: 用于存储所有合法解的指针数组(需预先分配内存) |
| 11 | + * @resSize: 当前已找到的解的数量(指针,用于修改外部变量) |
| 12 | + * @cols: 布尔数组,标记各列是否已被皇后占用 |
| 13 | + * @diags1: 布尔数组,标记各主对角线是否已被占用(索引公式:row - col + n - 1) |
| 14 | + * @diags2: 布尔数组,标记各次对角线是否已被占用(索引公式:row + col) |
| 15 | + * |
| 16 | + * 功能描述: |
| 17 | + * 1. 递归尝试在棋盘的每一行放置皇后,确保不互相攻击。 |
| 18 | + * 2. 使用列、主对角线、次对角线的标记数组进行剪枝。 |
| 19 | + * 3. 当找到一个合法解时,将其深拷贝到res中。 |
| 20 | + */ |
| 21 | +/* 回溯算法:n 皇后 */ |
| 22 | +void backtrack(int row, int n, char state[MAX_SIZE][MAX_SIZE], char ***res, int *resSize, bool cols[MAX_SIZE], |
| 23 | + bool diags1[2 * MAX_SIZE - 1], bool diags2[2 * MAX_SIZE - 1]) { |
| 24 | + // 当放置完所有行时,记录解 |
| 25 | + if(row == n) { |
| 26 | + res[*resSize] = (char **)malloc(sizeof(char *) * n); |
| 27 | + for (int i = 0; i < n; ++i) { |
| 28 | + res[*resSize][i] = (char *)malloc(sizeof(char) * (n + 1)); |
| 29 | + strcpy(res[*resSize][i], state[i]); |
| 30 | + } |
| 31 | + (*resSize)++; |
| 32 | + return; |
| 33 | + } |
| 34 | + // 遍历所有列 |
| 35 | + for (int col = 0; col < n; col++) { |
| 36 | + // 计算该格子对应的主对角线和次对角线 |
| 37 | + int diag1 = row - col + n - 1; |
| 38 | + int diag2 = row + col; |
| 39 | + // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 |
| 40 | + if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { |
| 41 | + // 尝试:将皇后放置在该格子 |
| 42 | + state[row][col] = 'Q'; |
| 43 | + cols[col] = diags1[diag1] = diags2[diag2] = true; |
| 44 | + // 放置下一行 |
| 45 | + backtrack(row + 1, n, state, res, resSize, cols, diags1, diags2); |
| 46 | + // 回退:将该格子恢复为空 |
| 47 | + state[row][col] = '#'; |
| 48 | + cols[col] = diags1[diag1] = diags2[diag2] = false; |
| 49 | + } |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +/** |
| 54 | + * nQueens - 求解N皇后问题的入口函数 |
| 55 | + * @n: 棋盘的大小(N x N)及皇后的数量 |
| 56 | + * @returnSize: 返回解的数量的指针(输出参数) |
| 57 | + * |
| 58 | + * 功能描述: |
| 59 | + * 1. 初始化棋盘状态和标记数组 |
| 60 | + * 2. 分配结果存储空间 |
| 61 | + * 3. 调用回溯算法求解所有合法布局 |
| 62 | + * 4. 返回解数组 |
| 63 | + * |
| 64 | + * 返回值: |
| 65 | + * 三维字符数组指针,存储所有合法的棋盘布局。 |
| 66 | + * 每个解是一个二维字符数组,表示一个棋盘状态。 |
| 67 | + * 调用者需负责释放返回的内存。 |
| 68 | + * |
| 69 | + * 注意: |
| 70 | + * - 使用MAX_SIZE宏限制最大棋盘尺寸 |
| 71 | + * - 棋盘初始化为全'#'(空位) |
| 72 | + * - 对角线数组大小为2n-1以覆盖所有可能对角线 |
| 73 | + */ |
| 74 | +/* 求解 n 皇后 */ |
| 75 | +char ***nQueens(int n, int *returnSize) { |
| 76 | + char state[MAX_SIZE][MAX_SIZE]; |
| 77 | + // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 |
| 78 | + for (int i = 0; i < n; ++i) { |
| 79 | + for (int j = 0; j < n; ++j) { |
| 80 | + state[i][j] = '#'; |
| 81 | + } |
| 82 | + state[i][n] = '\0'; |
| 83 | + } |
| 84 | + |
| 85 | + bool cols[MAX_SIZE] = {false}; // 记录列是否有皇后 |
| 86 | + bool diags1[2 * MAX_SIZE - 1] = {false}; // 记录主对角线上是否有皇后 |
| 87 | + bool diags2[2 * MAX_SIZE - 1] = {false}; // 记录次对角线上是否有皇后 |
| 88 | + |
| 89 | + char ***res = (char ***)malloc(sizeof(char **) * MAX_SIZE); |
| 90 | + *returnSize = 0; |
| 91 | + backtrack(0, n, state, res, returnSize, cols, diags1, diags2); |
| 92 | + return res; |
| 93 | +} |
| 94 | + |
| 95 | +/* Driver Code */ |
| 96 | +int main(int argc, char **argv) { |
| 97 | + // int n = 4; |
| 98 | + if (argc != 2) { |
| 99 | + fprintf(stderr, "Usage: %s <n>\n", argv[0]); // 使用 argv[0] 显示程序名 |
| 100 | + return 1; |
| 101 | + } |
| 102 | + |
| 103 | + int n = atoi(argv[1]); // 使用 argv[1] 获取用户输入 |
| 104 | + if (n <= 0) { |
| 105 | + fprintf(stderr, "Error: n must be positive\n"); |
| 106 | + return 1; |
| 107 | + } |
| 108 | + |
| 109 | + int returnSize; |
| 110 | + char ***res = nQueens(n, &returnSize); |
| 111 | + |
| 112 | + printf("输入棋盘长宽为%d\n", n); |
| 113 | + printf("皇后放置方案共有 %d 种\n", returnSize); |
| 114 | + |
| 115 | + for(int i = 0; i < returnSize; ++i) { |
| 116 | + for (int j = 0; j < n; ++j){ |
| 117 | + printf("["); |
| 118 | + for (int k = 0; res[i][j][k] != '\0'; ++k) { |
| 119 | + printf("%c", res[i][j][k]); |
| 120 | + if (res[i][j][k + 1] != '\0') { |
| 121 | + printf(", "); |
| 122 | + } |
| 123 | + } |
| 124 | + printf("]\n"); |
| 125 | + } |
| 126 | + printf("---------------------\n"); |
| 127 | + } |
| 128 | + |
| 129 | + // 释放内存 |
| 130 | + for (int i = 0; i < returnSize; ++i) { |
| 131 | + for (int j = 0; j < n; ++j) { |
| 132 | + free(res[i][j]); |
| 133 | + } |
| 134 | + free(res[i]); |
| 135 | + } |
| 136 | + free(res); |
| 137 | + |
| 138 | + return 0; |
| 139 | +} |
0 commit comments