數據結構(二)棧與隊列---回溯法之八皇后問題


(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;
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM