回溯算法

概念

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
在这里插入图片描述

基本思想

从一条路往前走,能进则进,不能进则退回来,换一条路再试。回溯在迷宫搜索中使用很常见,就是这条路走不通,然后返回前一个路口,继续下一条路。回溯算法说白了就是穷举法。不过回溯算法使用剪枝函数,剪去一些不可能到达 最终状态(即答案状态)的节点,从而减少状态空间树节点的生成。回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。

解题步骤

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

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值