严格来说,搜索也是一种暴力枚举策略,传统的枚举需要固定for循环的层数,但是这样不能随意增减枚举层数,本文将介绍一种新的利用递归的方式枚举每个可能的选项,如果合法就继续下一个,如果所有选项都不合法就退回并尝试更换上一个的选项,继续枚举。这种方式就是回溯算法,常用深度优先搜索实现:

先来看一道模板题:

排列数字

给定一个整数 n,将数字 1∼n 排成一排,将会有很多种排列方法。

现在,请你按照字典序将所有的排列方法输出。

输入格式:共一行,包含一个整数 n。

输出格式:按字典序输出所有排列方案,每个方案占一行。

数据范围:1≤n≤7

思路:

想象有n个空位,需将n个数插入其中:_ _ _ _ _ _ _ _ _ _ _ 
我们用a表示空位,用step表示正在处理的空位,用book标记使用过的数(将book定义为全局,使用过的改成1,未使用的则默认为0)
很容易就能想到如下代码:
if(book[i]==0)    // 即未使用过{a[step]==i;book[i]==1;        }

那么下一个空位该如何处理呢?

首先我们将上面的代码封装成一个函数dfs:
void dfs(int step)
{for(int i=i;i<=n;i++)// 空位上可选择的数为1到n{if(book[i]==0){a[step]==i;book[i]==1;}}
}
那么处理下一个空位的方法就是递归 dfs(step+1):
void dfs(int step)
{for(int i=i;i<=n;i++){if(book[i]==0){a[step]==i;book[i]==1;dfs(step+1);book[i]==0;            // 这是重要的一步,收回已经使用的数也就是第step个数}}
}
book[i]=0非常重要,它的作用是收回刚才的数,因为当递归结束也即所有的空位都被占满时,我们就需要让出最后的空位来寻找新的排列方法,如果不回收这个数,那么就不能进行下一次的插入
递归的进入比较容易理解,但是递归的回溯是我们无法看到的。因此递归究竟是如何完成的,成为了理解递归的一大难点:
递归程序在回退完成前,return会使得计算机继续依次执行上一层函数调用后的代码,而返回上一层还能继续枚举的原因是计算机运行函数时为每个子函数都分配了专门存储递归函数信息的栈空间,其中包括了每层函数各个局部变量的值。
还有最后一个问题,就是什么时候该输出一个满足要求的序列呢?
答案就是当step==n+1时,此时前n的空位都已经填满,只需输出即可。
该函数如下
void dfs(int step)
{if(step==n+1){for(int i=1;i<=n;i++)cout<<a[i]<<' ';cout<<endl;return;        // 返回以前的一步}for(int i=i;i<=n;i++){if(book[i]==0){a[step]==i;book[i]==1;dfs(step+1);book[i]==0;}}
}
完整代码如下
#include<iostream>
using namespace std;
const int N=100;
int a[N],book[N],n;void dfs(int step)
{if(step==n+1){for(int i=1;i<=n;i++)cout<<a[i]<<' ';cout<<endl;return;}for(int i=1;i<=n;i++){if(book[i]==0){a[step]=i;book[i]=1;dfs(step+1);book[i]=0;}}
}
int main()
{cin>>n;dfs(1);return 0;
}

由此可得深搜的基本模型:

void dfs(int step)
{if(所有空被填完){记录答案/判断最优解 return; }for(枚举选项) if(合法) {记录现场 dfs(step+1);恢复现场 }
}

下面再来看一道经典题目:

八皇后

一个如下的6×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

上面的布局可以用序列 2 4 6 1 3 5 来描述,第 i 个数字表示在第 i 行的相应位置有一个棋子,如下:

行号1 2 3 4 5 6

列号 2 4 6 1 3 5

这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 3 个解。最后一行是解的总个数。

输入格式:一行一个正整数 n,表示棋盘是 n×n 大小的。

输出格式:前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

思路:

考虑到每一行、列只能放一个,对于行,我们可以遍历,对于列,我们可以递归,最后判断是否在同一条斜线上即可,斜线的判断是本题的难点,考虑到它们的斜率都为1,所以不同的斜线截距必然不同,我们可以套用初中的直线方程:y=x+b → b=y-x(对角)     y=-x+b→b=y+x(反对角)但是考虑到数组名不能为负,而且截距只是为了区分并无实际意义,所以对角的截距我们需要加上一个较大的数,代码:

#include<bits/stdc++.h>
using namespace std;//column列   diagonal对角   antidiagonal反对角
//y=x+b;b=y-x    dg
//y=-x+b;b=y+x  adg
const int N=15;
int a[N][N];
int col[N],dg[N*2],adg[N*2];
int res,n;
void dfs(int u)     // 从第u行开始
{if(u==n+1)  {   if(res<3){for(int i=1;i<=n;i++){                  for(int j=1;j<=n;j++)if(a[i][j]==1)cout<<j<<' ';}cout<<endl;   }       res++;        }for(int i=1;i<=n;i++)   //记录列 if(!col[i]&&!dg[i-u+n]&&!adg[u+i]){a[u][i]=1;col[i]=1;dg[i-u+n]=1;adg[u+i]=1;dfs(u+1);a[u][i]=0;col[i]=0;dg[i-u+n]=0;adg[u+i]=0;       }   }
int main()
{cin>>n;dfs(1);cout<<res<<endl;return 0;
}
另外再拓展一下:
_  _  _ + _  _  _ = _  _  _
将数字1到9填进空位,每个数只能用一次,问一共有几种组合?

直接上代码:


#include<iostream>
using namespace std;
const int N=100;
int a[N],book[N],res;void dfs(int step)
{if(step==10){if(a[1]*100+a[2]*10+a[3]+a[4]*100+a[5]*10+a[6]==a[7]*100+a[8]*10+a[9]){printf("%d%d%d+%d%d%d=%d%d%d\n",a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9]);res++;return;}}for(int i=1;i<10;i++){if(book[i]==0){a[step]=i;book[i]=1;dfs(step+1);book[i]=0;}}
}
int main()
{dfs(1);cout<<res/2<<endl;    //会出现a+b=c||b+a=c的重复情况return 0;
}

详解深度优先搜索与回溯相关推荐

  1. 深入浅出,详解深度优先搜索(DFS)

    深度优先搜索 如果把深度优先搜索比作一个人的话,那么这个人是一个执着的人,甚至倔强,不把一条路走到底不会返回(回溯),虽然执着倔强,但是他不傻,如果在探索的途中发现这条路走下去没有希望他会提前返回(剪 ...

  2. 深度优先搜索c语言详解,深度优先搜索 — C语言版

    思路:找一个入口结点,然后搜索该结点的第一个相邻结点,再搜索该相邻结点的第一个相邻结点,依次往下寻找 - - ,直到所有结点都被遍历到,算法结束,退出. #include #define MAX 10 ...

  3. 以SIGSEGV为例详解信号处理(与栈回溯)

    以SIGSEGV为例详解信号处理(与栈回溯) 信号是内核提供的向用户态进程发送信息的机制, 常见的有使用SIGUSR1唤醒用户进程执行子程序或发生段错误时使用SIGSEGV保存用户错误现场. 本文以S ...

  4. Nuist集训队作业:深度优先搜索(回溯算法)

    Nuist集训队第一次作业:深度优先搜索(回溯算法) 引例 深搜基本思想及回溯算法模板 P1706 全排列问题 P1219 八皇后 P1605 迷宫 P1101 单词方阵 小结 引例 国际西洋棋棋手马 ...

  5. C++解题报告:详解经典搜索难题——八数码问题( 双向BFS A* 求解)

    引言 AC这道八数码问题,你和楼教主就是兄弟了... 题目描述 在一个3*3的九宫格棋盘里,放有8个数码,数码的数字分别是1~8.棋盘中还有一个位置是空着的,用0表示.可以通过在九宫格里平移数码来改变 ...

  6. 一文详解 | 开放搜索兼容Elasticsearch做召回引擎

    简介:开放搜索发布开源兼容版,支持阿里云Elasticsearch做搜索召回引擎,本文详细介绍阿里云ES用户如何通过接入开放搜索兼容版丰富行业分词库,提升查询语义理解能力,无需开发.算法投入,即可获得 ...

  7. 回溯算法详解:理论+基础类回溯题解

    文章目录 五:括号生成问题 六:组合总和(点击跳转) 七:子集问题(点击跳转) 五:括号生成问题 这个问题也比较经典,题目的要求是让你生成所有可能的并且 有效的 括号组合. 对于括号问题,两个特性,请 ...

  8. 计算机 查找 功能的使用,详解电脑搜索快捷键是什么?

    办公软件Office使用经常要替换或更改文档上面的字眼,那么如何快速查找文档上面的相关的全部关键词呢?其实Office软件提供内置的搜索查找功能,能够快速定位关键词位置以及个数,甚至快速替换等等,下面 ...

  9. elasticsearch系列四:搜索详解(搜索API、Query DSL)

    一.搜索API 1. 搜索API 端点地址 从索引tweet里面搜索字段user为kimchy的记录 GET /twitter/_search?q=user:kimchy 从索引tweet,user里 ...

最新文章

  1. 每日英语:Relationship Repair: 10 Tips for Thinking Like a Therapist
  2. VC++ 访问数据库实例详解图解
  3. 检查本地服务器是否配置成功
  4. 【Flask】sqlalchemy 排序
  5. 【要闻】Kubernetes安全问题严峻、Linux v5.4安全性浅谈
  6. Java EE 6示例– Galleria第2部分
  7. 理论基础 —— 排序 —— 鸡尾酒排序
  8. Google 也要“勒紧腰带”过日子了!
  9. $.ajax+php实战教程之下拉时自动加载更多文章原理分析
  10. GPS 相关知识科普
  11. Onvif客户端与服务器通信时鉴权的自实现
  12. favicon.ico图标在线制作+ico下载
  13. [亲自试过有效] 错误1606。无法访问网络位置
  14. 计算机老提示安全证书到期,安全证书过期,教您怎么解决网站安全证书过期
  15. iphone邮件服务器 263,在iphone上怎么设置263邮箱
  16. (2.2)【窃密型木马-广外幽灵】简介、使用
  17. 在移动设备上使用M3G编程教程(转)
  18. matlab 柱状图不同颜色(取巧哈)
  19. python-读取和保存npy文件
  20. DDD微服务架构设计第四课 DDD指导微服拆分和落地实现

热门文章

  1. linux中 代表什么,Linux中的$1代表什么?
  2. 写给我的2014——也写给我即将逝去的研究生生涯
  3. 计算机音频服务未运行怎么办,音频服务未运行win10如何处理_win10电脑显示音频服务未运行怎么解决...
  4. 300服务器维护,怎么7月24号300英雄服务器进不去,一直在维护,要维护到几点啊
  5. 【jdbc】spring
  6. 数据结构(廿六) -- C语言版 -- 图 - 图的遍历 -- 邻接表 - 深度/广度优先遍历/搜索(DFS、BFS)
  7. 保险业务与系统——LOMA 290 保险公司运营——第十四讲——财务管理
  8. 我们应该用什么酒袋来安全地运输葡萄酒?
  9. SSL协议、TLS协议,使用哪一种更安全?
  10. 5分钟掌握智联招聘网站爬取并保存到MongoDB数据库