回溯法,简单理解就是有源可溯。基本思想要借鉴穷举法,但是它不是一味地穷举,当发现某一步不符合条件时,这一步后面的穷举操作就不进行了(俗称“剪枝”),我自己把它叫做动态穷举法。假设第一个步骤可行,那么执行第二个步骤,第三个......如果其中第三个步骤不行,那么我们再回过来(回溯),第二个步骤换一种方法尝试,然后再重新第三个步骤,第四个......直到完成任务要求为止。
这里,以八皇后问题为例。试图把回溯法讲清楚。
注意:递归应该是一种算法结构,回溯法是一种算法思想。
八皇后问题
八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。当且仅当 n = 1 或 n ≥ 4 时问题有解。
解题思想
我们采用回溯法来解决这个问题。还记得我说的动态穷举法(“剪枝”)?那么我们就开始穷举吧。过程请看下图。八个皇后,每个皇后放一行,那么我们要确定的就是每行皇后要放的列的位置。对于第一行,假设把皇后放在第一列(这里就开始了一个for循环了)。第一步当然满足,然后我们看第二行(又开始一个for循环啦),假设把第二个皇后放在(2,1)(行,列)处,不行(“剪枝”)!那继续for,放在(2,2)处,不行(“剪枝”)!继续for,放在(2,3)处。Bingo!那我们进行第三步,第四步....也许,在第三步当中,执行完第三步的八次for循环后,仍未有合理的答案。那么就得返回第二步了。这时候,把第二个皇后放在(2,4)处。继续......
这里,以八皇后问题为例。试图把回溯法讲清楚。
注意:递归应该是一种算法结构,回溯法是一种算法思想。
八皇后问题
八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。当且仅当 n = 1 或 n ≥ 4 时问题有解。
解题思想
我们采用回溯法来解决这个问题。还记得我说的动态穷举法(“剪枝”)?那么我们就开始穷举吧。过程请看下图。八个皇后,每个皇后放一行,那么我们要确定的就是每行皇后要放的列的位置。对于第一行,假设把皇后放在第一列(这里就开始了一个for循环了)。第一步当然满足,然后我们看第二行(又开始一个for循环啦),假设把第二个皇后放在(2,1)(行,列)处,不行(“剪枝”)!那继续for,放在(2,2)处,不行(“剪枝”)!继续for,放在(2,3)处。Bingo!那我们进行第三步,第四步....也许,在第三步当中,执行完第三步的八次for循环后,仍未有合理的答案。那么就得返回第二步了。这时候,把第二个皇后放在(2,4)处。继续......
#include<iostream>
#include<Windows.h>
using namespace std;
int m[8][8];
int queen[8];
void print()
{
system("cls");
cout << "八皇后问题动态演示\n";
cout << "------------------------\n";
for (int outer = 0; outer < 8; outer++)
{
if (queen[outer] != -1)
{
for (int inner = 0; inner < queen[outer]; inner++)
cout << " . ";
cout << " # ";
}
for (int inner = queen[outer] + 1; inner < 8; inner++)
cout << " . ";
cout << endl;
}
}
bool is_meet(int i,int j)
{
int r, c;
for (r = 0; r < i; r++)
{
c = queen[r];
if (j == c)
return 0;
if ((i + j) == (c + r))
return 0;
if ((j-i) == (c - r))
return 0;
}
return 1;
}
void findQueen(int i)
{
Sleep(1000);
print();
int j;
for (j = 0; j < 8; j++)
{
if (is_meet(i, j))
{
queen[i] = j;
if (i == 7)
{
//print();
return;
}
else
findQueen(i+1);
}
queen[i] = -1;
}
}
int main()
{
memset(queen, -1, 8 * sizeof(int));//这里是赋-1,故不会出错,要清楚memset是依次对单个字节进行赋值
findQueen(0);
system("pause");
}
本文通过八皇后问题详细介绍了回溯法的基本思想及其实现过程。利用动态穷举法(剪枝)来减少不必要的搜索,提高了求解效率。
1524

被折叠的 条评论
为什么被折叠?



