回溯1-N皇后问题

N皇后原题

回溯实际上是一种试探算法,这种算法跟暴力搜索最大的不同在于,在回溯算法里,是一步一步地小心翼翼地进行向前试探,会对每一步探测到的情况进行评估,如果当前的情况已经无法满足要求,那么就没有必要继续进行下去,也就是说,它可以帮助我们避免走很多的弯路。
回溯算法的特点在于,当出现非法的情况时,算法可以回退到之前的情景,可以是返回一步,有时候甚至可以返回多步,然后再去尝试别的路径和办法。这也就意味着,想要采用回溯算法,就必须保证,每次都有多种尝试的可能。
回溯不是单线的运算的顺序,而是像“树”一样的运算顺序
搜索的过程不用for()循环(因为这样要套多层的循环且循环的次数是固定的),而是用函数的循环调用
清除搜索过的方法,一般可以采用在“函数内的调用函数”的后面,看起来就像是这个:

以下是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdbool.h>
#define MAX 100
int putincol[MAX] = {0}; // putincol[i]存的是第i行的棋子在第几列
bool used[MAX] = {0};    // uesd[j]存的是第j列是否有棋子,有为true,无为false
int n, count = 0; //这里用全局变量省的用指针
void process(int raw)
{
    if (raw > n)
    { // 每次row大于n时说明一种棋子的摆法可以了,而所有情况结束没有条件,因为没有死循环的存在,为什么这么说呢,
      // 因为如果row有加一和不加两种情况,如果不加,函数停止(通过if绕开递归的循环),如果raw加一了,直到raw大于n进入另一个if,有return结束
        count++;
        printf("第%d种情况为:\n", count);
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                if (putincol[i] == j)
                    printf("1");
                else
                    printf("0");
            }
            printf("\n");
        }
    }
    for (int j = 1; j <= n; j++)
    {                     // 每一行都是一个新的起点,这里是函数递推的地方
        bool flag = true; // 用排除法,假设一开始是可以摆棋子,但是下面的条件只要满足了一项就不可以摆棋子
        for (int k = 1; k < raw; k++)
        {
            if (abs(raw - (raw - k)) == abs(j - putincol[raw - k]))
            {
                flag = false;
                break;
            }
        } // 判断是否斜的在一条线上
        if (used[j] == true)
            flag = false; // 判断是否同列
        if (flag)
        {                      // 如果flag == true ,说明可以摆棋子
            putincol[raw] = j; // 摆下
            used[j] = true;    // 占位
            process(raw + 1);  // 关键步骤1
            used[j] = false;   // 关键步骤2:回溯
            // 解答一下putincol[]为什么不用回溯的问题:used[]回溯是因为如果其中有一个分支不是解(出现这种情况肯定是它的下一行全部都不是解),
            // 那就说明这个位子不行,就要把它删掉,而在这种情况下的putincol[]也的确是要删掉的,但是for循环的j会把这一行的putincol[raw]的数据刚好盖掉,就不用删掉了,
            // 而used[j]是第j列的数据和for循环里的数据不是相匹配的
        }
    }
}
int main()
{
    scanf("%d", &n);
    process(1);
    printf("一共有%d种情况\n", count);
    system("pause");
}

变体1

题目描述

会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。

如何将 8 个皇后放在棋盘上(有 8×8 个方格),使它们谁也不能被吃掉!

这就是著名的八皇后问题。

对于某个满足要求的 8 皇后的摆放方法,定义一个皇后串 a 与之对应,即 a=b1b2…b8,其中 bi 为相应摆法中第 i 行皇后所处的列数。

已经知道 8 皇后问题一共有 92 组解(即 92 个不同的皇后串)。

给出一个数 b,要求输出第 b 个串。

串的比较是这样的:皇后串 x 置于皇后串 y 之前,当且仅当将 x 视为整数时比 y 小。

输入格式

第一行包含整数 n,表示共有 n 组测试数据。

每组测试数据占 1 行,包括一个正整数 b。

输出格式

输出有 n 行,每行输出对应一个输入。
输出应是一个正整数,是对应于 b 的皇后串。

输入样例

2
1
92

输出样例

15863724
84136275

源代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdbool.h>
#define MAX 100
int putincol[MAX] = {0}; 
bool used[MAX] = {0};    
int count = 0;
void process(int raw, int n)
{
    if (raw > 8)
    {
        count++;
        if (count == n)
        {
            for (int i = 1; i <= 8; i++)
            {
                printf("%d", putincol[i]);
            }
            printf("\n");
        }
    }
    for (int j = 1; j <= 8; j++)
    {                     
        bool flag = true; 
        for (int k = 1; k < raw; k++)
        {
            if (abs(raw - (raw - k)) == abs(j - putincol[raw - k]))
            {
                flag = false;
                break;
            }
        } 
        if (used[j] == true)
            flag = false; 
        if (flag)
        {                 
            putincol[raw] = j;   
            used[j] = true;      
            process(raw + 1, n); 
            used[j] = false; 
        }
    }
}
int main()
{
    int n, b;
    scanf("%d", &n);
    while (n--)
    {
        scanf("%d", &b);
        process(1, b);
        for (int i = 1; i < 8; i++)
        {
            putincol[i] = 0;
            used[i] = 0;
        }
        count = 0;
    }
    system("pause");
}

变体2

题目描述

规则同8皇后问题,但是棋盘上每格都有一个数字,要求八皇后所在格子数字之和最大。

输入格式

一个8*8的棋盘。
数据规模和约定
棋盘上的数字范围0~99

输出格式

所能得到的最大数字和

输入样例

1 2 3 4 5 6 7 8
9 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24
25 26 27 28 29 30 31 32
33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48
48 50 51 52 53 54 55 56
57 58 59 60 61 62 63 64

输出样例

260

Answer:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#define MAX 10
#define max(a, b) a > b ? a : b
int chess_number[MAX][MAX] = {0};
int putincol[MAX] = {0};
bool used[MAX] = {0};
int sum = 0;
void process(int row)
{
    if (row > 8)
    {
        int temp = 0;
        for (int i = 1; i <= 8; i++)
        {
            temp += chess_number[i][putincol[i]]; //主要是这里不同
        }
        sum = max(sum, temp);
    }
    for (int j = 1; j <= 8; j++)
    {
        bool flag = true;
        for (int k = 1; k <= row; k++)
        {
            if (abs(row - (row - k)) == abs(j - putincol[row - k]))
            {
                flag = false;
                break;
            }
        }
        if (used[j] == true)
            flag = false;
        if (flag)
        {
            putincol[j] = j;
            used[j] = true;
            process(row + 1);
            used[j] = false;
        }
    }
}
int main()
{
    for (int i = 1; i <= 8; i++)
    {
        for (int j = 1; j <= 8; j++)
        {
            scanf("%d", &chess_number[i][j]);
        }
    }
    process(1);
    printf("%d\n", sum);
    system("pause");
    return 0;
}
posted @ 2023-01-10 16:57  MITE's_BKY  阅读(54)  评论(0)    收藏  举报