万老师C语言笔记:二维数组:八皇后 幻方 迷宫问题

一、数组知识回顾

一维数组

二维数组

二、八皇后问题

八皇后问题是国际西洋棋棋手马克 斯·贝瑟尔于1848年提出: 在8×8格的国际象棋上摆放八个皇 后,使其不能互相攻击,即任意两个皇 后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
高斯认为有76种方案。1854年在柏林的 象棋杂志上不同的作者发表了40种不同的解 ,后来有人用图论的方法解出92种结果。 计算机发明后,有多种计算机语言可以 解决此问题。

数据结构:二维数组?一维数组?

方案一:二维数组

用一个8行8列的二维数组存储8*8的国际 象棋棋盘,数组元素初值为0;0-代表没有放皇后 1-代表放置了皇后。
int board[MAX_N][MAX_N];
例如:
chess[0][5]=1;//在第0行第5列放一个皇后
chess[1][1]=1;//在第1行第1列放一个皇后

方案二:一维数组

用下标代表行,值代表列。只要数组每个值各不相同,就说明不在同一行和列,从而简化了冲突的判断,只需要考虑是否对角线冲突

实际上等价于行号差与列号差的绝对值相同。因此可这样进行冲突检测:
if (queen[i] == col || abs(queen[i] - col) == abs(i - row))
那么如何摆放呢?

三、枚举方法

最适合计算机也一定不会遗漏任何一种摆放的方法!摆放方式共有:8×8 ×8 ×8 ×8 ×8 ×8 ×8 = 8^8
用八层for循环枚举。
分析:同一列没有必要再摆放,若第n个皇后与前面冲突,则不用进行n+1个皇后的摆放,而应回到上一步。(回溯思想)

    void queen() {
    int chess[8], i; // chess数组存放皇后的摆放,i用于输出皇后的循环变量

    for (chess[0] = 0; chess[0] <= 7; chess[0]++) // 对应第0行的8种摆放
        for (chess[1] = 0; chess[1] <= 7; chess[1]++)
            for (chess[2] = 0; chess[2] <= 7; chess[2]++)
                for (chess[3] = 0; chess[3] <= 7; chess[3]++)
                    for (chess[4] = 0; chess[4] <= 7; chess[4]++)
                        for (chess[5] = 0; chess[5] <= 7; chess[5]++)
                            for (chess[6] = 0; chess[6] <= 7; chess[6]++)
                                for (chess[7] = 0; chess[7] <= 7; chess[7]++)
                                    if (check(chess, 8) == 1) // 如果不存在冲突则输出这种摆放方案
                                        for (i = 0; i < 8; i++) // 输出每一行皇后的位置
                                            printf("%d ", chess[i]);}

这段代码通过嵌套的循环尝试所有可能的皇后摆放方式。对于每种摆放方式,它调用名为 check 的函数,检查是否存在皇后之间的冲突(即是否存在在同一行、同一列或对角线上)。如果没有冲突,它就打印出每一行皇后的位置。

四、回溯方法

    void queen() {
    int chess[8], i; // chess存放皇后的摆放 i用于输出皇后的循环变量

    // 循环尝试每一行的摆放方式
    for (chess[0] = 0; chess[0] <= 7; chess[0]++) {
        // 对应第0行的8种摆放
        for (chess[1] = 0; chess[1] <= 7; chess[1]++) {
            // 如果存在冲突,则看下一种摆放
            if (check(chess, 2) == 0)
                continue;
            else
                // 循环尝试第2行的摆放方式
                for (chess[2] = 0; chess[2] <= 7; chess[2]++) {
                    // 如果存在冲突,则看下一种摆放
                    if (check(chess, 3) == 0)
                        continue;
                    else
                        // 循环尝试第3行的摆放方式
                        // ... (以此类推,一直到第7行)
                        for (chess[7] = 0; chess[7] <= 7; chess[7]++) {
                            // 如果第8个皇后和前面7个冲突则看下一种摆放,否则输出这种摆放方式
                            if (check(chess, 8) == 0)
                                continue;
                            else
                                // 输出当前的摆放方式
                                for (i = 0; i <= 7; i++)
                                    print(chess[i]);
                        }
                }
        }
    }
    }
    // 函数check用于检查当前皇后的摆放是否与前面的皇后有冲突
    int check(int a[], int n) {
    // 实现检查的逻辑,如果有冲突返回0,否则返回1}

    // 函数print用于输出当前皇后的摆放
    void print(int queenPosition) {
    // 输出当前皇后的位置}

八重for循环 只能解决8皇后,如果变成N皇后呢?
有一种思路:
放下第1个皇后,还需要放下N-1个皇后;
放下第2个皇后,还需要放下N-2个皇后;
放下了第N个皇后,结束了(递归出口)
递归的回溯算法分析:实际上是一个DFS算法

最终代码(一维数组实现):

    #include <stdio.h>
    #include<math.h>
    #define N 4
    int queen[N];
    bool isSafe(int row, int col) {
    // 检查同一列是否有皇后
    for (int i = 0; i < row; i++) {
        if (queen[i] == col || abs(queen[i] - col) == abs(i - row)) {
            return false;
        }
    }
    return true;}

    int placeQueen(int row) {
    int count = 0;
    if (row == N) {
        // 所有皇后都已经放置,找到一个解法       
        return 1;
    }

    for (int col = 0; col < N; col++) {
        if (isSafe(row, col)) {
            // 在当前位置放置皇后
            queen[row] = col;
            // 递归放置下一行的皇后
            count+=placeQueen(row + 1);
            
        }
    }
    return count;}
    int main()
    {
    int count=placeQueen(0);
    printf("%d", count);
    return 0;
    }

五、幻方问题

幻方是指将若干个自然数排成纵横各为若干个数的正方形,使在同一行、同一列和同一对角线上的几个数的和都相等。此时我们只考虑奇幻方问题。可见:洛谷P2615
简单来说就是将1放在第一行的中间,下一个数放在上一个数“右上角”,若已被占,放在正下方。
例如,三阶幻方为:
8 1 6
3 5 7
4 9 2
/代码实现/

#include<stdio.h>
int n, a[40][40], x, y;
 int main() {
scanf("%d", &n);
x = 1, y = (n + 1) / 2;
for (int i = 1; i <= n * n; i++) {
	a[x][y] = i;
	if (!a[(x - 2 + n) % n + 1][y % n + 1])
		{
			x = (x - 2 + n) % n + 1, y = y % n + 1;
		}
	else x = x % n + 1;//数学运算
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
    }

以下是if法题解,采用了动态内存分配。

#include <stdio.h> 
#include <stdlib.h>

int main()
    {
    int n;
    scanf("%d", &n);

    if (n <= 0)
    {
        printf("错误的输入。请输入一个正整数。");
        return 1;
    }

    int** a = (int**)malloc(sizeof(int*) * n);
    if (a == NULL)
    {
        printf("内存分配失败");
        return 1;
    }

    for (int i = 0; i < n; i++)
    {
        a[i] = (int*)malloc(sizeof(int) * n);
        if (a[i] == NULL)
        {
            printf("内存分配失败");
            return 1;
        }
    }
    int i = 0;
    int j = (n - 1) / 2;
    a[i][j] = 1;
    for (int k = 2; k <= n * n; k++)
    {
        if (i == 0 && j != n - 1)
        {
            i = n - 1;
            j++;
        }
        else if (i != 0 && j == n - 1)
        {
            i--;
            j = 0;
        }
        else if (i == 0 && j == n - 1)
        {
            i++;
        }
        else if (i != 0 && j != n - 1)
        {
            if (i-1>=0&&i-1<n&&j+1>=0&&j<n&&(!(a[i - 1][j + 1] >= 1 && a[i - 1][j + 1] <= n * n)))
            {
                i--;
                j++;
            }
            else
            {
                i++;
            }
        }
        a[i][j] = k;
    }

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            printf("%d ", a[i][j]);
        }
        printf("\n");
    }

    for (int i = 0; i < n; i++)
    {
        free(a[i]);
    }
    free(a);

    return 0;}

六、迷宫问题

迷宫问题显然无法用一维数组解决,因为同一行不只有一个“皇后”。采用DFS思想,先试探一个方向,若走不通,再返回上一步试探其它方向。

posted @ 2024-08-23 14:43  Losyi  阅读(38)  评论(0编辑  收藏  举报