深度优先搜索(DFS)
深度优先搜索就好比走迷宫, 不断顺着一条路走, 直到走不通为止, 然后回退到上一个路口再向另外的方向行走(走过的方向就不会再走了,又不是傻子, 知道走不通,还向走不通的方向走), 不断重复(试过所有路口, 状态转移), 重复直到找到唯一的一条合适的路径;
DFS可以看做是二叉树的先序遍历, 多说无益; 直接上题;
例1:
给定a1, a2, a3,…an个数, 从中选出若干个数, 使他们的和恰好为K
输入: n=4; a={1,2,4,7}; K=13;
输出: 2+4+7=13
分析: 当初我看到这道题, 就是懵逼状态, 唉, DFS还没搞清楚就开始做题, 很蠢, 不扯那些没用的, 回归主题: 从给出的例子来看, 既然我们要从1,2,4,7中选出若干个数, 那就可以暴力枚举, 循环列举出a集合的幂集, 找出满足条件的答案; 但是这种方法的复杂度让我瘆得慌, 复杂度为O(n^n), so, 这种做法就果断放弃, 从今天的主题来看势必要用到DFS来解决问题(我这不废话吗, 唉), 从DFS的定义来看就是状态转移, 这里的状态转移表示的是每个数都拥有两个选择的权利, 要么加, 要么不加, 就好比走迷宫有两条路, 要么朝左, 要么朝右, 你把这个过程想象走迷宫, 每种选择都对应着不同的走法, 总有一条路是对的(看成一棵长得像二叉树一样的迷宫), 因此复杂度为2的n次方, 相比前面的n的n次方下降了很多, 尽管下降了很多但是在实际应用需要做剪枝优化, 就题论事有如下代码:
#include<iostream>
using namespace std;
int n=4,a[4]={1,2,4,7},k=13;
bool dfs(int i,int sum){if(i==n)return sum==k;//满足条件然后进行回归if(dfs(i+1,sum)) {//在交叉口做选择
// cout<<" "<<a[i]<<endl;
// 如果没加就不用输出a[i],忽略了这个问题 return true;}if(dfs(i+1,sum+a[i])){cout<<" "<<a[i]<<endl;return true;}return false;
}
int main(){cout<<dfs(0,0)<<endl;return 0;
}
上述整个过程可看做二叉树的前序遍历, 当满足条件便回归
深度优化搜索总是从最开始的状态出发,遍历所有可能到达的状态, 由此可对所有产生的状态进行操作
例2:
这道题是深度优化搜索的一个很好例子, 拿来作为参考
分析: 以第一个题作为启示, 第一个题可看做单连通域进行遍历, 总共只在main函数中使用了一次dfs, 而这道题当遇到积水时就需要使用dfs, 一次dfs就能对相连的’w’进行遍历,直到一个连通域遍历完成, 如图总共有3个连通域, 多说无益看代码:
#include<iostream>
using namespace std;
int N=10,M=12;
char field[10][12]={"w.......ww.",".www....ww.","...w.....w.",".........w.",".........w.","wwww.....ww","www.......w","ww........w","ww........w","w.........."};
void dfs(int x,int y){field[x][y]='.';for(int dx=-1;dx<=1;dx++){for(int dy=-1;dy<=1;dy++){//针对当前'w'的8个方向使用dfs, 直到一个连通域遍历完成int nx=x+dx,ny=y+dy;if(0<=nx&&nx<N&&0<=ny&&ny<M&&field[nx][ny]=='w')//满足条件开始遍历dfs(nx,ny);}}return;
}
int main(){int res=0;for(int i=0;i<N;i++){for(int j=0;j<M;j++){if(field[i][j]=='w'){//针对整个地图使用dfsdfs(i,j);++res;}}}cout<<res<<endl;
}
每个格子至多被调用一次, 所以复杂度O(N*M)
总的来说, 深度优化搜索就是对图结构进行遍历(以二叉树的前序遍历的方式), 直到遇到符合条件的情况