回溯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;
}

浙公网安备 33010602011771号