深度优先搜索 模板+例题

dfs板子

bool check(参数) {
  if(满足条件)
    return ture;
  return false;
}
void dfs(int step) {
  if(到达边界){
    输出或其他相关操作 //根据题意添加
    return ;
  }
  if(越界 / 不合法的状态)
    return;
for() {
  if(满足check) {
  修改操作; //根据题意判断是否执行该操作 **1**
  标记;
  dfs(step + 1);//继续下一步
  (还原标记) //根据题意判断是否执行该操作 **2**
                         //如果加上(还原标记)就是 回溯法 
  }
 }
}

例题1 选数

题目描述
已知 n 个整数 x_1,x_2,x_n,以及1个整数k(k<n)。从n个整数中任选k个整数相加,可分别得到一系列的和。例如当n=4,k=3,4个整数分别为3,7,12,193,7,12,19时,可得全部的组合与它们的和为:

3+7+12=22

3+7+19=29

7+12+19=38

3+12+19=34

现在,要求你计算出和为素数共有多少种。

例如上例,只有一种的和为素数:3+7+19=293+7+19=29。

输入输出样例
输入
4 3
3 7 12 19
输出
1

#include<iostream>
#include<cstring>
int a[22], p[22];
bool vis[22];
int n, k,sum,ans;
using namespace std;
bool isprime(int n) { //判断素数
        if(n <= 1)  return false;
        for(int i = 2; i  * i  <= n; ++i) 
           if(n % i == 0) 
               return false;
            return true;
}
void dfs(int step){
        if(step == k + 1){
          if(isprime(sum))
            ans++;
            return ;//返回之前的一步(最近一次调用dfs函数的地方)
        }
        for(int i = 1; i <= n; ++i) {
           if(vis[i] == false && i  > p[step - 1]) {
              p[step] = i;
              sum += a[i];
              dfs(step + 1);
              vis[i] = false;
              sum -= a[i];
           }
        }
}
int main() {
     memset(vis,0,sizeof(vis));//初始化
     cin>>n>>k;
     for(int i = 1; i <= n; ++i) {
       cin>>a[i];
       p[i] = i;
     }
     ans = 0;
     dfs(1);
     cout<<ans<<endl;
     return 0; 
}

例题2 迷宫

题目背景
给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过。给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。

题目描述

输入格式
第一行N、M和T,N为行,M为列,T为障碍总数。第二行起点坐标SX,SY,终点坐标FX,FY。接下来T行,每行为障碍点的坐标。

输出格式
给定起点坐标和终点坐标,问每个方格最多经过1次,从起点坐标到终点坐标的方案总数。

输入输出样例
输入
2 2 1
1 1 2 2
1 2
输出
1
说明/提示
【数据规模】

1≤N,M≤5
PS:把图画出来推导更方便,也可以用广搜来做

#include<iostream>
int a[6][6], book[6][6];
int n, m, t, p, q, sum;
using namespace std;
void dfs(int x, int y, int step) {
   int next[4][2] = {{0,1}, {1,0}, {0,-1}, {-1,0}};
   int tx, ty;
   if(x == p && y == q) { //判断是否到达终点
      sum++; //到达终点,方案数加一
      return ;
   }
   for(int i = 0; i < 3; ++i){
      tx = x + next[i][0];
      ty = y + next[i][1];//计算下一个点的坐标
      if(tx < 1 || tx > n || ty < 1 || ty > m)  continue; //判断是否越界
      if(a[tx][ty] == 0 && book[tx][ty] == 0) {
        book[tx][ty] = 1; //标记
        dfs(tx, ty, step); //尝试下一个点
        book[tx][ty] = 0;//取消标记
      } 
   }
    return;
}
int main(void){
    int sx, sy, fx, fy;
    cin>>n>>m>>t;
    cin>>sx>>sy>>p>>q;
    book[sx][sy]  = 1; //标记该点已走,防止重复走
    for(int i = 1; i <= t; ++i){
       cin>>fx>>fy;
       a[fx][fy] = 1; //障碍物的初始化
    }
    dfs(sx, sy, 0);// 参数一起点横坐标。二 起点纵坐标,三 初始化步数为0
    cout<<sum<<endl; 
    return 0;
}

例题3 油田

题意翻译
【题目大意】输入多个m行n列的矩阵,用00表示输入结束。找出有多少块石油区域,用“@”代表石油,假如两个“@”在横,竖或对角线上相邻,就说它们位于同一区域,对于每个输入,输出一个数表示有几个石油区域。

感谢@songhn 提供的翻译

输入输出样例
输入 #1复制
1 1

3 5
#@#@#
##@##
#@#@#
1 8
@@####@#
5 5
####@
#@@#@
#@##@
@@@#@
@@##@
0 0
输出 #1复制
0
1
2
2
PS由于博客格式限制,这里改了样例的输入符号
点此进入原题

#include<iostream>
#include<cstring>
char a[103][103];
int n ,m;
using namespace std;
bool check(int x, int y) {
   if(x >= 0 && x <= m && y >= 0 && a[x][y] == '@') //判断是否是油田
      return 1;
   return 0;
}
int dfs(int x, int y){
   int next[8][2] = {{1, 0},{0,1},{-1,0},{0,-1},{1,-1},{-1,1},{-1,-1},{1,1}};
   int tx, ty;
   if(check(x, y)) {
     a[x][y] = '.';
     for(int i = 0; i < 8; ++i) {
       tx = x + next[i][0];
       ty = y + next[i][1];
       dfs(tx, ty);
     }
    return 1;
    }
    return 0;
}
int main() {
   while(cin>>m>>n && (m != 0 && n != 0)) { // 简单粗暴地判断终止输入的条件
     int sum = 0; //切记在循环内部初始化 
     memset(a, 0, sizeof(a));
     for(int i = 0; i < m; ++i){
       cin>>a[i];
     }
     for(int i = 0; i  < m; ++i) {
          for(int j = 0; j  < n; ++j) {
             if(dfs(i, j)) sum++;//发现油田 
          }
       }
      cout<<sum<<endl;
   }
   return 0;
 }

感谢前辈

深度优先搜索 模板+例题深度优先搜索 模板+例题 竒仔 发布了2 篇原创文章 · 获赞 0 · 访问量 47 私信 关注
上一篇:【Leetcode 深搜、广搜】岛屿的最大面积(695)


下一篇:题解 洛谷P5380 【[THUPC2019]鸭棋】