回溯算法初步

本文详细介绍了回溯算法和深度优先搜索的概念,通过全排列和n皇后问题的实例展示了算法的实现过程。回溯法是一种通过尝试所有可能情况并适时回退来解决问题的方法。在全排列问题中,通过状态表示和状态恢复实现搜索;而在n皇后问题中,考虑了额外的冲突条件判断。文章提供了完整的C++代码示例,帮助读者理解算法的应用。
摘要由CSDN通过智能技术生成

深度优先搜索DFS

从问题某种可能情况出发,搜索所有能到达的可能情况,然后以其中一种可能的情况为新的出发点,继续向下探索,当所有可能情况都探索过且无法到达目标的时候,再回退到上一个出发点,继续探索另一个可能情况,这种不断回头寻找目标的方法称为"回溯法"。

相关概念

  • 状态 :状态即事物所表现出来的形态
  • 状态空间:所有状态(结点)的集合,如何存储的问题
  • 问题状态空间树:是指用树的结构来表示所有的问题状态,树中的每个结点确定了所求解的一个问题状态,树中有些结点代表的是答案状态,即是那些满足求解条件的状态

算法框架-递归回溯

DFS(状态);
{
	if(目标状态){处理;}
	else for each path do
		if(可行){
			修改状态;
			DFS(新状态);
			状态恢复;
		}
}
main(){
	DFS(初始状态);
}

回溯算法实现关键

  1. 状态表示
  2. 目标状态
  3. 路径列表
  4. 可行性判定
  5. 修改状态
  6. 状态恢复

例题1-全排列

1. 采用搜索路径进行可行性判定

int n,arr[999] = {0};	//全局变量	
//状态a数组
//step为当前填第几个数
//将第step个数填入的数组下标为step-1

void DFS(int step){
    //目标状态判断 大于n时输出
    if(step>n){		//当填入的数的个数大于给定n时,即结束,输出一次结果。
        for(int i = 0;i<n;i++)
            cout<<a[i]<<" ";
        cout<<endl;
        return;
    }
	//从1到n开始找可行数
    for(int i = 1;i<=n;i++){	//枚举路径列表
        bool f = true;	//可行标记
        for(int j = 0;j<step-1;++j)   		//查看前方数组内是否含有欲找的可行数 
        //当前欲在step-1处填入第step个数,那么已经填好的数的下标则为从0到step-2 循环判断
            if(a[j]==i)						//如果已有与该值相同的数被填充
                f = false;	
        //修改状态
        if(f){  							//如果没有与该值相同的数被填充,则选取该数
            a[step-1] = i;    				//将其放入下标step-1位置
            DFS(step+1);    				//递归求数组内下一位
            //状态恢复 进行下一次循环
            a[step-1] = 0;    //目标状态判定结束且输出后 层层回退使数组值为0
        }
    }
}


int main(){
    cin>>n;
    DFS(1);
    for(int i = 0;i<n;i++)	//这里打印全0
        cout<<a[i]<<" ";
    return 0;
}

2. 采用数组标记进行可行性判定

int n,a[999] = {0},vh[999] = {0};
void DFS(int step){
    //目标状态判断 大于n时输出
    if(step>n){
        for(int i = 0;i<n;i++)
            cout<<a[i]<<" ";
        cout<<endl;
        return;
    }
    for(int i = 1;i<=n;i++){    //枚举路径列表
        if(vh[i]==0){       //对应数组vh下标的值为0,则表示可行
            a[step-1] = i;      //将其放入位置
            vh[i] = 1;          //修改值为1
            DFS(step+1);        //递归求数组内下一位
            //状态恢复 进行下一次循环
            vh[i] = 0;          //一次循环后,层层回退,使标记全部置0
            //这里不再需要将a[step-1]值0
        }
    }
}

int main(){
    cin>>n;
    DFS(1);
    for(int i = 0;i<n;i++)	//此时打印最后一次结果
        cout<<a[i]<<" ";
    return 0;
}

例题2-n皇后问题

问题描述:在n×n格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
解决思路:

  • 每列只有一个皇后,可以用一维数组来记录每列皇后所在的行,用数组的下标表示所在的列(从1开始),那么它的解一定在全排列的解中
    ㅤㅤ
  • 但多出的约束条件是要判断是否在同一斜线上发生冲突,现假设在第k列第i行上有一皇后,即arr[k] = i,下面将第j列皇后放在第arr[j]行上,则冲突条件为:arr[j]==i || abs(i-arr[j])==abs(j-k),即当第arr[j]行和第i行为同一行或者行差等于列差时,发生冲突。

下面,在全排列基础上对判断条件进行修改:

#include "<bits/stdc++.h>"
using namespace std;
int n,arr[999] = {0};	//全局变量

void DFS(int step){ //step为列号 数组从下标1开始存储
    //目标状态判断 大于n时输出
    if(step>n){		//当填入的数的个数大于给定n时,即结束,输出一次结果。
        for(int i = 1;i<=n;i++)
            cout<<arr[i]<<" ";
        cout<<endl;
        return;
    }
	//从1到n开始找可行数
    for(int i = 1;i<=n;i++){	//枚举路径列表 枚举行数
        bool f = true;	//可行标记
        for(int j = 1;j<step;++j)   		//查看前方数组内是否含有欲找的可行数
            if(arr[j]==i || abs(step-j)==abs(arr[j]-i))		//行号a[j]和前面存在相同或在同一斜线上时
                f = false;
        //修改状态
        if(f){  							//如果没有与该值相同的数被填充,则选取该数
            arr[step] = i;    				//将其放入下标step-1位置
            DFS(step+1);    				//递归求数组内下一位
            //状态恢复 进行下一次循环
            arr[step] = 0;    //目标状态判定结束且输出后 层层回退使数组值为0
        }
    }
}


int main(){
    cin>>n;
    DFS(1);
    
    for(int i = 1;i<=n;i++)	//这里打印全0
        cout<<arr[i]<<" ";
    return 0;
}

  • ——————END-2022-01-07——————
  • 个人学习笔记,如有纰漏,敬请指正。
  • 感谢您的阅读。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苡荏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值