(0)預備知識
C語言復習---二維數組和二級指針的關系:沒關系,別瞎想(重點)
(一)問題描述
要在8*8的國際象棋棋盤中放8個皇后,使任意兩個皇后都不能互相吃掉。規則是皇后能吃掉同一行、同一列、同一對角線的棋子。如下圖即是兩種方案:

(二)遞歸代碼實現
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #define ERROR 0 #define OK 1 typedef int Status; int count = 0; /* int *chess[8] 代表棋盤,chess代表每一行指針 int row是當前行 int col是當前列 上面確定了一個位置 */ Status noDanger(int(*chess)[8], int row, int col) { int i = 0; int r, c; //先判斷當前列 for (i = 0; i < 8; i++) if (chess[i][col] != 0) return ERROR; //再將當前行設置為-1 for (i = 0; i < 8; i++) if (chess[row][i] != 0) return ERROR; //再將斜行列設置為-1,分為4個方向, //左上:行列減一, r = row; c = col; while (r >= 0 && c >= 0) { if (chess[r][c] != 0) return ERROR; r--; c--; } //右上:行減一列加一, r = row; c = col; while (r >= 0 && c < 8) { if (chess[r][c] != 0) return ERROR; r--; c++; } //左下:行加一列減一, r = row; c = col; while (r < 8 && c >= 0) { if (chess[r][c] != 0) return ERROR; r++; c--; } //右下:行加一列加一, r = row; c = col; while (r < 8 && c < 8) { if (chess[r][c] != 0) return ERROR; r++; c++; } return OK; } /* int *chess[8] 代表棋盤,chess代表每一行指針 int row是當前行 int col是當前總列數 */ void EightQueen(int(*chess)[8], int row, int cols) { int chess2[8][8]; int i, j; for (i = 0; i < 8;i++) for (j = 0; j < 8;j++) chess2[i][j] = chess[i][j]; //row==8是遞歸結束條件 if (row==8) //從0-7就判斷完畢,到第九行是不存在的,說明已經找到所有的位置,可以打印了 { printf("the number of %d ways\n", count + 1); for (i = 0; i < 8;i++) { for (j = 0; j < 8; j++) printf("%d ", *(*(chess2 + i) + j)); printf("\n"); } printf("\n"); count++; //獲取的正確方案加一 } else { //判斷當前行的每一列是否有效 for (j=0;j<cols;j++) { if (noDanger(chess, row, j)) //若是這個位置不危險,可以存放皇后 { for (i = 0; i < 8;i++) { *(*(chess2 + row) + i) = 0; //將這一行設為0 } *(*(chess2 + row) + j) = 9; //當前存放皇后的位置設置為9 //進行遞歸,繼續向下 EightQueen(chess2, row + 1, cols); } } } } int main() { int chess[8][8] = { 0 }; EightQueen(chess, 0, 8); printf("end\n"); system("pause"); return 0; }
遞歸函數EightQueen
void EightQueen(int(*chess)[8], int row, int cols) { int chess2[8][8]; //由於可以走到的方案太多,我們最好使用一個臨時變量去存儲原來棋盤位置,不然每次保存現場也是釋放消耗時間空間的 int i, j; for (i = 0; i < 8;i++) for (j = 0; j < 8;j++) chess2[i][j] = chess[i][j]; //row==8是遞歸結束條件 if (row==8) //從0-7就判斷完畢,到第九行是不存在的,說明已經找到所有的位置,可以打印了 { printf("the number of %d ways\n", count + 1); for (i = 0; i < 8;i++) { for (j = 0; j < 8; j++) printf("%d ", *(*(chess2 + i) + j)); printf("\n"); } printf("\n"); count++; //獲取的正確方案加一 } else { //判斷當前行的每一列是否有效 for (j=0;j<cols;j++) //我們在參數中,獲取了應該操作的行數,現在我們應該對列進行循環,判斷該行中列的正確與否 { if (noDanger(chess, row, j)) //若是這個位置不危險,可以存放皇后 //雖然我們大多使用的是臨時棋盤chess2,但在這里我們需要使用到參數傳遞過來的上級棋盤,因為我們在判斷合法位置后,會修改臨時棋盤,導致臨時棋盤該位置被填充,變為不合法。不會再進入循環 { for (i = 0; i < 8;i++) { *(*(chess2 + row) + i) = 0; //將這一行設為0 //我們在這里修改了臨時棋盤,所以我們在上面判斷位置合法性,不要使用臨時棋盤,而是使用上級棋盤chess } *(*(chess2 + row) + j) = 9; //當前存放皇后的位置設置為9 //進行遞歸,繼續向下 EightQueen(chess2, row + 1, cols); //繼續遞歸,傳入臨時棋盤 } } } }
位置合法性noDanger
我們需要對該位置的行,列,斜行列進行判斷,其中斜線上我們需要判斷左上,右上,左下,右下四個方向
Status noDanger(int(*chess)[8], int row, int col) { int i = 0; int r, c; //先判斷當前列 for (i = 0; i < 8; i++) if (chess[i][col] != 0) return ERROR; //再將當前行設置為-1 for (i = 0; i < 8; i++) if (chess[row][i] != 0) return ERROR; //再將斜行列設置為-1,分為4個方向, //左上:行列減一, r = row; c = col; while (r >= 0 && c >= 0) { if (chess[r][c] != 0) return ERROR; r--; c--; } //右上:行減一列加一, r = row; c = col; while (r >= 0 && c < 8) { if (chess[r][c] != 0) return ERROR; r--; c++; } //左下:行加一列減一, r = row; c = col; while (r < 8 && c >= 0) { if (chess[r][c] != 0) return ERROR; r++; c--; } //右下:行加一列加一, r = row; c = col; while (r < 8 && c < 8) { if (chess[r][c] != 0) return ERROR; r++; c++; } return OK; }

