概念
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
基本思想
从一条路往前走,能进则进,不能进则退回来,换一条路再试。回溯在迷宫搜索中使用很常见,就是这条路走不通,然后返回前一个路口,继续下一条路。回溯算法说白了就是穷举法。不过回溯算法使用剪枝函数,剪去一些不可能到达 最终状态(即答案状态)的节点,从而减少状态空间树节点的生成。回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。
解题步骤
1.定义一个解空间,它包含问题的解;
2.利用适于搜索的方法组织解空间;
3.利用深度优先法搜索解空间;
4.利用限界函数避免移动到不可能产生解的子空间。
练习
1.八皇后问题(递归与回溯)
该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
思路:
用flag[n]=col表示第n行的皇后放在了第col列,这样只需要判断列和上下对角线有没有皇后就可以了,这里上下对角线都为15份,这样可以用两个一维数组来表示上下对角线是否有皇后
上对角线:n-col+7表示数组对角线
下角标:n+col表示数组下标
#include <iostream>
using namespace std;
int place[8]={0};//该数组的值表示第几行的皇后在第几列,如place[2]=1;表示第三行的皇后在第2列
bool flag[8]={1,1,1,1,1,1,1,1};
bool d1[15]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
bool d2[15]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
int nums=0;//记录可行的皇后组
int NumOfQueen(int n)
{
int col;
for(col=0;col<8;col++)
{
if(flag[col]&&d1[n-col+7]&&d2[n+col]) //判断该位置是否可以放置皇后
{
place[n]=col;
flag[col]=false;
d1[n-col+7]=false;
d2[n+col]=false;
if(n<7)
NumOfQueen(n+1);
else
nums++;
//回溯,寻找其他可行组
flag[col]=true;
d1[n-col+7]=true;
d2[n+col]=true;
}
}
return nums;
}
int main()
{
cout << NumOfQueen(0);
return 0;
}
2数组问题
问题描述:给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。(不包含重复的数组)
示例:
输入: nums = [1,2,3]
输出:
[3]
[1]
[2]
[1,2,3]
[1,3]
[2,3]
[1,2]
[]
#include <vector>
#include <iostream>
using namespace std;
void gerenget(int i,vector<int> &item,vector<int>&nums,vector<vector<int>>&result)
{
if(i>=nums.size())
return ;
item.push_back(nums[i]);
result.push_back(item);
//第一次调用
gerenget(i+1,item,nums,result);
item.pop_back();
//进行第二次调用
gerenget(i+1,item,nums,result);
}
int main()
{
vector<int> nums;
int a[3]={1,2,3};
for(int i=0;i<3;i++)
{
nums.push_back(a[i]);
}
vector<int>item;
vector<vector<int>> result;
result.push_back(item);
gerenget(0,item,nums,result);
cout << "[]" << endl;
for(int i=1;i<result.size();i++)
{ cout << "[";
cout << result[i][0];
for(int j=1;j<result[i].size();j++)
cout <<","<<result[i][j];
cout << "]";
cout << endl;
}
return 0;
}