DFS
简述
DFS(Depth-First-Search)深度优先搜索,是搜索算法的一种,用于遍历图中的节点,DFS类似于树的先序遍历,是树的先序遍历的推广。
算法思想
- 访问顶点v;
- 依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;
- 若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。
模板代码
void dfs(int i,int j,int num){
//不满足条件,结束
if(j == k){
return;
}
for(int l = i ; l < n;l++){
if(!vis[l]){//判断是否访问过
vis[l] = 1; //标记访问
dfs(l,j+1,num+arr[l]);//递归循环
vis[l] = 0;//回溯
}
}
}
题目类型
- POJ1088:滑雪
Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长的滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子
1 2 3 4 5 16 17 18 19 6 15 24 25 20 7 14 23 22 21 8 13 12 11 10 9
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-…-3-2-1更长。事实上,这是最长的一条。
输入:
输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。
输出:
输出最长区域的长度。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int arr[105][105],r,c,sum = -1,mark[105][105] = {0};
int x[] = {-1,1,0,0},y[] = {0,0,-1,1};
int dfs(int i,int j){
int val;
if(mark[i][j] != 0){
return mark[i][j];
}
for(int k = 0;k<4;k++){
if( i+x[k] >= 0 && i+x[k] < r && arr[i][j] > arr[i+x[k]][j]){
val = dfs(i+x[k],j) +1;
if(val > mark[i][j])
mark[i][j] = val;
}
}
for(int k = 0;k<4;k++){
if( j+y[k] >= 0 && j+y[k] < c && arr[i][j] > arr[i][j+y[k]]){
val = dfs(i,j+y[k])+1;
if(val > mark[i][j]) mark[i][j] = val;
}
}
return mark[i][j];
}
int main(){
cin>>r>>c;
for(int i = 0 ; i < r;i++){
for(int j = 0 ;j < c;j++){
cin>>arr[i][j];
}
}
sum = 0;
for(int i = 0 ; i < r ; i ++){
for(int j = 0 ; j < c;j++){
int l = dfs(i,j)+1;
if(l > sum){
sum = l;
}
}
}
cout<<sum<<endl;
return 0;
}
或者超时写法:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int arr[105][105],vis[105][105] = {0},r,c,sum = -1;
int x[] = {-1,1,0,0},y[] = {0,0,-1,1};
void dfs(int i,int j,int num){
if(i < 0 || j < 0 || i > r || j > c){
if(sum < num){
sum = num;
}
return;
}
for(int k = 0;k<4;k++){
if(!vis[i+x[k]][j] && arr[i][j] > arr[i+x[k]][j]){
vis[i+x[k]][j] = 1;
dfs(i+x[k],j,num+1);
vis[i+x[k]][j] = 0;
}
}
for(int k = 0;k<4;k++){
if(!vis[i][j+y[k]] && arr[i][j] > arr[i][j+y[k]]){
vis[i][j+y[k]] = 1;
dfs(i,j+y[k],num+1);
vis[i][j+y[k]] = 0;
}
}
}
int main(){
cin>>r>>c;
for(int i = 0 ; i < r;i++){
for(int j = 0 ;j < c;j++){
cin>>arr[i][j];
}
}
sum = 0;
for(int i = 0 ; i < r ; i ++){
for(int j = 0 ; j < c;j++){
dfs(i,j,0);
}
}
cout<<sum<<endl;
return 0;
}
- N皇后问题
请设计一种算法,解决著名的n皇后问题。这里的n皇后问题指在一个n*n的棋盘上放置n个棋子,
使得每行每列和每条对角线上都只有一个棋子,求其摆放的方法数。
输入:
给定一个int n,请返回方法数,保证n小于等于15
输出:
方法数
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
using namespace std;
int map[20],key;
bool check(int r)
{
for (int i = 1; i < r; i++)
{
if(map[i] == map[r]||abs(i-r)==abs(map[i]-map[r]))
return false;
}
return true;
}
void dfs(int n,int r)
{
if(r == n+1){
key++;
}
for(int i = 1;i<=n;i++)
{
map[r] = i;
if(check(r))
dfs(n,r+1);
}
}
int main()
{
int n;
scanf("%d",&n);
memset(map,0,sizeof(map));
key = 0;
dfs(n,1);
cout<<key;
return 0;
}
解析:
首先八皇后问题是只需要判断同一行同一列还有对角线上没有棋子即可,所以利用map数组的下标标识了行号,而里边存放的内容标识了列号,因为同一行不可有,可省略判断,所以在
if(map[i] == map[r]||abs(i-r)==abs(map[i]-map[r]))
代码中,map[i] == map[r]
代表了同一列不存在相同的数据。对角线的判断相对比较难理解一些,因为对角线的除了满足x+y相等意外,还满足对角线上两个点的相应下标相减的绝对值相等,例如(1,2)与(2,3)abs(1-2) = abs(2-3),所以abs(i-r)==abs(map[i]-map[r])
是为了判断是否在同一条对角线上。最后通过递归回溯,得出此问题的最终解。
总结
DFS的基本思想就是从某一点开始,依次遍历满足条件的节点,如果符合遍历条件,则以当前遍历节点为节点,重复上述操作,直到达到最终解或者不满足条件,跳出递归,回到父节点处(回溯),继续遍历节点寻找满足条件的节点。
总结一句话就是,以某一节点为起始点,深度遍历所有节点,直到遍历完该节点下所有节点,结束遍历。