目录

  • 一、DFS三个经典NPC问题
    • 1.子集和问题
    • 2.全排列问题
    • 3.N皇后问题
    • luogu P1219 [USACO1.5]八皇后 Checker Challenge
    • luogu P1562 还是N皇后(状态压缩)
  • 二、DFS习题
  • 1.AcWing 165. 小猫爬山
  • AcWing 166. 数独

声明:
本系列博客是《算法竞赛进阶指南》+《算法竞赛入门经典》+《挑战程序设计竞赛》的学习笔记,主要是因为我三本都买了 按照《算法竞赛进阶指南》的目录顺序学习,包含书中的少部分重要知识点、例题解题报告及我个人的学习心得和对该算法的补充拓展,仅用于学习交流和复习,无任何商业用途。博客中部分内容来源于书本和网络(我尽量减少书中引用),由我个人整理总结(习题和代码可全都是我自己敲哒)部分内容由我个人编写而成,如果想要有更好的学习体验或者希望学习到更全面的知识,请于京东搜索购买正版图书:《算法竞赛进阶指南》——作者李煜东,强烈安利,好书不火系列,谢谢配合。


下方链接为学习笔记目录链接(中转站)

学习笔记目录链接


ACM-ICPC在线模板


深度优先搜索算法是一种用于遍历或搜索树或图的算法

建立一颗搜索树,沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。

一、DFS三个经典NPC问题

三个经典深搜入门级问题。
子集和问题,全排列问题,N皇后问题。

1.子集和问题

子集和问题的一个实例为<S,c>。其中S={x1,x2,…,xn}是一个正整数的集合,c是一个正整数。子集和问题判定是否存在S的一个子集S1,使得S1中所有元素的和为c。

跟0x03递归那一节的差不多回溯递归

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<vector>
#include<unordered_map>
#define ls (p<<1)
#define rs (p<<1|1)
//#pragma GCC optimize (2)
//#pragma G++ optimize (2)//手动开 O2
#define over(i,s,t) for(register int i = s;i <= t;++i)
#define lver(i,t,s) for(register int i = t;i >= s;--i)
//#define int __int128
using namespace std;
#undef mid
typedef long long ll;
typedef pair<int,int> PII;
const int N = 30007;
const ll mod = 1e9+7;
const ll INF = 1e15+7;
const double EPS = 1e-10;
const int base = 131;//13331
int n,m,sum;
int a[N];
bool vis[N],flag;
void calc(int x){if(flag||vis[x]||x>n+1)return ;if(sum == m){flag = 1;over(i,1,n){if(vis[i])printf("%d ",a[i]);}return;}calc(x+1);//不选sum += a[x];vis[x] = 1;calc(x+1);//选vis[x] = 0;//回溯sum -= a[x];
}
int main()
{scanf("%d%d",&n,&m);over(i,1,n)scanf("%d",&a[i]);calc(1);if(!flag)puts("NO");return 0;
}

2.全排列问题

给定一个由不同的小写字母组成的字符串,输出这个字符串的所有全排列。

这个懒得敲了,直接复制一个网上的代码好了

#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>
using namespace std;char allprem[1000][7];
int count = 0;void swap(char* str,int a,int b)
{char tmp = str[b];str[b] = str[a];str[a] = tmp;
}
void perm(char* str,int start,int end)
{if(start == end - 1){strcpy(allprem[count],str);count++;}else{for(int k = start;k < end;k++){swap(str,k,start);perm(str,start+1,end);swap(str,k,start);}}
}int compare(const void * a,const void* b)
{return strcmp((char*)a,(char*)b);
}
int main()
{char inputStrr[7] = {0};while(cin >> inputStrr){   count = 0;perm(inputStrr,0,strlen(inputStrr));qsort(allprem,count,sizeof(allprem[0]),compare);for(int i = 0;i < count;i++)cout << allprem[i] << endl;}return 0;
}

3.N皇后问题

luogu P1219 [USACO1.5]八皇后 Checker Challenge

洛谷题目链接

基础的dfsdfsdfs回溯,关键是对角线上的处理(如何区分左对角线和右对角线)。
处理方法:
对角线上左上的对角线i+ji+ji+j为定值,右上的对角线i−ji-ji−j也为定值,为防止小于零所以用i−j+ni-j+ni−j+n,一样为定值。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<vector>
#include<unordered_map>
#define ls (p<<1)
#define rs (p<<1|1)
#pragma GCC optimize (2)
#pragma G++ optimize (2)
#define over(i,s,t) for(register int i = s;i <= t;++i)
#define lver(i,t,s) for(register int i = t;i >= s;--i)
//#define int __int128
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;const int N = 30007;
const ll mod = 1e9+7;
const ll INF = 1e15+7;
const double EPS = 1e-10;
const int base = 131;int n,m;
int a[100];
int check[3][100];
int sum;
int tot;
void dfs(int now){if(now>n){tot++;if(tot > 3)return ;over(i,1,n)printf("%d ",a[i]);puts("");return ;}over(i,1,n){if(!check[0][i]&&!check[1][now+i]&&!check[2][now-i+n]){a[now] = i;check[0][i] = 1,check[1][i+now] = 1,check[2][now-i+n] = 1;dfs(now+1);check[0][i] = 0,check[1][i+now] = 0,check[2][now-i+n] = 0;}}
}
int main()
{scanf("%d",&n);dfs(1);printf("%d\n",tot);return 0;
}

luogu P1562 还是N皇后(状态压缩)

洛谷题目链接

这里我所习惯的二进制还是右边是第0位开始,这样正好与实际情况相反,所以都要反着来。

  1. 逐行放置皇后,首先排除每行有多个皇后互相排斥的情况

  2. 用二进制表示状态.1表示该点不能放(与其他位置的皇后排斥或初始状态就不能放).0表示该点可以放皇后

  3. 用sta[]来存储初始状态,将’.'位 置为1

  4. dfs保存四个参数:当前行的状态,从左上到右下对角线的状态,从右上到左下对角线的状态,当前为第几行

  5. 获取当前哪一位可以放置皇后:将四者取并集(即将四者进行或运算).得到的状态中为0的就可以放置皇后.

  6. 为了快速得到可以放置皇后的位置,对上一步得到的状态进行取反.转换成快速得到1的位置.

  7. 用树状数组中的lowbit()就可以得到从右向左的第一个1

  8. 将状态中的1减掉,继续找下一个1

  9. 更新"将是下一行的状态",由于对角线是斜着影响的,所以左上到右下对角线的状态需要右移一位,右上到左下对角线的状态需要左移一位.

  10. 知道当前行的状态全为1时,即每一行都有一个皇后时,ans++;

思路
还有就是涉及到位运算状态压缩我还是习惯于从第0位开始,所以所有的循环都要统一从0开始。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<vector>
#include<unordered_map>
#define ls (p<<1)
#define rs (p<<1|1)
#pragma GCC optimize (2)
#pragma G++ optimize (2)
#define over(i,s,t) for(register int i = s;i <= t;++i)
#define lver(i,t,s) for(register int i = t;i >= s;--i)
//#define int __int128
#define lowbit(p) p&(-p)
using namespace std;
#undef mid
typedef long long ll;
typedef pair<int,int> PII;const int N = 100;
const ll mod = 1e9+7;
const ll INF = 1e15+7;
const double EPS = 1e-10;
const int base = 131;int n,m;
char ch[N];
int state[N];
int ach,ans;void dfs(int now,int l,int r,int line){if(now == ach){ans++;return ;}int pos = ach&(~(now|l|r|state[line]));//pos里1的可以放置皇后int p;while(pos){//把pos里每一个1都取出来p = lowbit(pos);pos -= p;dfs(now+p,(l+p)>>1,(r+p)<<1,line+1);}
}
int main()
{scanf("%d",&n);ach = (1<<n)-1;//从0到n-1全部放完for(int i = 0;i < n;++i){scanf("%s",ch);getchar();over(j,0,n-1)if(ch[j] == '.')state[i] |= (1<<(j));//第j位 置1}dfs(0,0,0,0);printf("%d\n",ans);return 0;
}

二、DFS习题

1.AcWing 165. 小猫爬山


直接搜索,因为只有两个状态,一个是利用已有的缆车,一个是新增一辆缆车。就直接爆搜,把所有的情况都搜一遍,取最优解。注意回溯。
但是直接爆搜会T,需要简单地剪枝,首先如果当前搜的过程中答案已经大于最优解,很显然可以直接returnreturnreturn。
然后就是因为小猫的重量不同,那么肯定是先为最重的小猫安排车会是最优的选项,所以应该先把小猫按照重量从大到小排个序再开始爆搜。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<vector>
#include<unordered_map>
#define ls (p<<1)
#define rs (p<<1|1)
#pragma GCC optimize (2)
#pragma G++ optimize (2)
#define over(i,s,t) for(register int i = s;i <= t;++i)
#define lver(i,t,s) for(register int i = t;i >= s;--i)
//#define int __int128
#define lowbit(p) p&(-p)
using namespace std;
#undef mid
typedef long long ll;
typedef pair<int,int> PII;const int N = 100;
const ll mod = 1e9+7;
const ll INF = 1e15+7;
const double EPS = 1e-10;
const int base = 131;int a[N];
int n,w;
int ans;
int cab[N];void dfs(int now,int cnt){if(cnt>=ans)return ;//剪枝if(now>n){ans = min(ans,cnt);return ;}//两个状态,一个是不租新的车,但可能不满足for(int i = 1;i <= cnt;++i){if(cab[i] + a[now] <= w){cab[i] += a[now];dfs(now+1,cnt);cab[i] -= a[now];//回溯}}//租一辆新车cab[cnt+1] = a[now];dfs(now+1,cnt+1);cab[cnt+1] = 0;//回溯
}int main()
{scanf("%d%d",&n,&w);over(i,1,n)scanf("%d",&a[i]);sort(a+1,a+1+n);reverse(a+1,a+1+n);ans = n;//最多用n辆车,最后取最小值所以要先最大化dfs(1,0);printf("%d\n",ans);return 0;
}

AcWing 166. 数独


首先需要从当前能填合法数字最小的位置开始填数字
排除等效冗余:任意一个状态下,我们只需要找一个位置填数即可,而不是找所有的位置和可填的数字.
位运算:很明显这里面check判定很多,我们必须优化这个check,所以我们可以对于,每一行,每一列,每一个九宫格,都利用一个九位二进制数保存,当前还有哪些数字可以填写.
lowbit:我们这道题目当前得需要用lowbit运算取出当前可以能填的数字.

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<vector>
#include<unordered_map>
#define ls (p<<1)
#define rs (p<<1|1)
#pragma GCC optimize (2)
#pragma G++ optimize (2)
#define over(i,s,t) for(register int i = s;i <= t;++i)
#define lver(i,t,s) for(register int i = t;i >= s;--i)
//#define int __int128
#define lowbit(p) p&(-p)//返回的是一个二进制数,通过预处理的num数组可以把这个数再转换回整数
using namespace std;
#undef mid
typedef long long ll;
typedef pair<int,int> PII;const int N = 100;
const ll mod = 1e9+7;
const ll INF = 1e15+7;
const double EPS = 1e-10;
const int base = 131;char str[N][N];
char s[N];
int n,m;
int num[1000],cnt[1000];
int hang[N],lie[N],ge[N],tot;inline int getone(int x,int y){//得到是第几个九宫格return ((x/3)*3)+(y/3);//因为这里是从0开始的0~8,如果是从1开始的要先-1
}//1是能放置,0是不能放置inline void calc(int x,int y,int z){//二进制修改第z位(从第0位开始的第z位)hang[x]^=1<<z;//行lie[y]^=1<<z;//列ge[getone(x,y)]^=1<<z;//九宫格
}inline bool dfs(int now){if(now == 0)return 1;int tmp = 10,x,y;over(i,0,8)over(j,0,8){if(str[i][j] != '.')continue;int val = hang[i]&lie[j]&ge[getone(i,j)];if(!val)return 0;if(cnt[val]<tmp){//找到当前能够合法填上的数字最小(cnt[i]最小,1最少)的位置tmp = cnt[val];x = i,y = j;}}int val = hang[x]&lie[y]&ge[getone(x,y)];for(;val;val-=lowbit(val)){//遍历所有的1int z = num[lowbit(val)];str[x][y] = '1'+z;calc(x,y,z);//修改if(dfs(now-1))//下一位return 1;calc(x,y,z);//回溯,再改回来str[x][y] = '.';}return 0;
}int main()
{for(int i = 0;i<1<<9;++i)for(int j = i;j;j -= lowbit(j))cnt[i]++;//对于每一个二进制数i,一共有几个 1 这里有几个1就是代表数是几(0~9)over(i,0,8)num[1<<i] = i;//lowbit返回的是一个二进制数,通过预处理的num数组可以把这个数再转换回整数,既是将要填写的数while(~scanf("%s",s)&&s[0]!='e'){over(i,0,8)over(j,0,8)str[i][j] = s[i*9+j];//还原成矩阵over(i,0,8)hang[i] = lie[i] = ge[i] = (1<<9)-1;//初始化为全部都是1(全部都能取)tot = 0;over(i,0,8)over(j,0,8){if(str[i][j] != '.')calc(i,j,str[i][j]-'1');//这一位不能取,置为 0 else tot++;//记录一共要填几个数}dfs(tot);over(i,0,8)over(j,0,8)s[i*9+j] = str[i][j];//还原为一维数组输出puts(s);}return 0;
}

注:如果您通过本文,有(qi)用(guai)的知识增加了,请您点个赞再离开,如果不嫌弃的话,点个关注再走吧,日更博主每天在线答疑 ! 当然,也非常欢迎您能在讨论区指出此文的不足处,作者会及时对文章加以修正 !如果有任何问题,欢迎评论,非常乐意为您解答!( •̀ ω •́ )✧

0x22.搜索 - 深度优先搜索相关推荐

  1. networkx 有向图强连通_leetcode刷题(四):搜索(深度优先搜索,广度优先搜索)拓扑排序,强连通分量...

    在开始今天的话题之前,我们先了解一个概念,什么是图的遍历? 图的遍历就是从图中某一点出发访遍图中其余剩余定点,且每个顶点仅被访问一次,这个过程叫做图的遍历. 图的遍历主要被分为深度优先遍历和广度优先遍 ...

  2. 搜索 —— 深度优先搜索(DFS)

    [概述] 深度优先搜索,是从初始状态起,利用一定的规则生成搜索树,寻找下一层任一个结点,检查是否出现目标状态,若未出现,以此状态利用规则生成再下一层任一个结点,再检查,重复过程一直到叶节点(即不能再生 ...

  3. 常用算法2 - 广度优先搜索 深度优先搜索 (python实现)

    1. 图 定义:图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合. 简单点的说:图由节点和边组成.一 ...

  4. leetcode算法入门 第七天 广度优先搜索/深度优先搜索

    leecode练习 733.图像渲染 695.岛屿最大面积 733.图像渲染 这道题题目很绕,其实就是一道深度优先搜索,将像素值相同的所有连通区域修改成新的像素值,只需要记录旧的像素值进行递归遍历即可 ...

  5. 每日刷题总结——广度优先搜索 / 深度优先搜索

    目录 286. 墙与门 417. 太平洋大西洋水流问题 1469. 寻找所有的独生节点 582. 杀掉进程 863. 二叉树中所有距离为 K 的结点 752. 打开转盘锁 1319. 连通网络的操作次 ...

  6. 剑指offer面试题12. 矩阵中的路径(矩阵搜索)(深度优先搜索)(剪枝)

    题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左.右.上.下移动一格.如果一条路径经过了矩阵的某一格,那么该 ...

  7. 优先深度搜索判断曲线相交_程序员必知的十大基础实用算法之-DFS(深度优先搜索)...

    深度优先搜索 深度优先搜索是一种在开发爬虫早期使用较多的方法.它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链的HTML文件) .在一个HTML文件中,当一个超链被选择后,被链接的HTML文件 ...

  8. c语言中穷竭算法,hihocoder#1054 : 滑动解锁(深度优先搜索)

    描述 滑动解锁是智能手机一项常用的功能.你需要在3x3的点阵上,从任意一个点开始,反复移动到一个尚未经过的"相邻"的点.这些划过的点所组成的有向折线,如果与预设的折线在图案.方向上 ...

  9. 深度优先搜索知识总结

    2019独角兽企业重金招聘Python工程师标准>>> 深度优先搜索 深度优先搜索是对图的一种遍历方式,如命所示,只要有可能,就尽可能的"深入".以下为<算 ...

最新文章

  1. oc75--不可变字典NSDictionary
  2. php模板技术smarty,PHP模板技术Smarty
  3. fir.im Weekly - 做一款 App 需要考虑什么
  4. Java04-day04【IDEA(概述、启动配置、项目结构、内容辅助键、快捷键、模块操作)、数组(定义详解、动态初始化、访问)、内存分配、数组内存图】
  5. 系统运维:收集6款经典的服务器监控工具
  6. php快速开发框架津县,BetePHP:一个轻量级快速开发框架
  7. 用户可计算型出题程序
  8. module_param的使用
  9. 目前还有机会火的6大短视频内容方向
  10. 关于php车服务论文,「PHP」行车服务app后端代码简析
  11. 使用react定义组件的两种方式
  12. 数据库设计-逻辑结构设计
  13. ECO生态币官网blog.sina.com.cn/ecocoin
  14. 如何用TC群控系统同时控制上百台手机
  15. python实战-网站扫码登录流程全解析
  16. Fastadmin读取数据库配置
  17. MSDC 4.3 接口规范(19)
  18. python房价预测_人工智能python实现-预测房价:回归问题
  19. jQuery实现购物车功能(小计、总计)
  20. 陕西二本计算机软件工程,高考分数不理想,仍然想报考软件工程专业,这四所二本大学不错...

热门文章

  1. 大数据背后的神秘定理:贝叶斯公式
  2. 不用3D建模软件,如何用数学公式创造一个女孩?会眨眼,有光影的那种
  3. MaxCompute Studio使用心得系列7—作业对比
  4. Xcode10:Implicit declaration of function '' is invalid in C99
  5. RHEL 5服务篇—修改MySQl数据库root密码的几种方式
  6. 让智能手机和居家电脑互联互通(WM6 GPRS)
  7. 云计算集成过程中七个关键性的问题
  8. Flash 在 EK 被利用漏洞 Top 10 中占 8 个位置
  9. Node.js process 模块常用属性和方法
  10. Linux运维人员成长之路学习书籍推荐