【算法基础】深搜(上)

简介: 【算法基础】深搜(上)


引语:

本篇文章从迭代,递归,再到深搜,由浅入深结合例题介绍。如果是零基础的,建议从头看完,这样到后面更好理解,如果递归学的较好的话也可以跳过前面的递归部分。

在各种算法竞赛或者面试中,总会有一些题目要求我们给出某个问题的各种方案以及方案的个数,对于数量级比较小的题目,我们可以采用枚举之类的暴力解法,但是对于数量级到达n的三次方及以上的题目甚至是阶乘级的题目就要小心了,因为暴力求解很可能超时。

所以,我们可以用试探性解法,当某一条支路提前确定走不通的时候,我们就不必继续向下进行了。也可以理解为“不撞南墙不回头”,或者“先把一条路走到黑”。

虽然看起来比暴力枚举更快一点,但是《算法竞赛入门经典》这本书将这种方法一并归到“暴力求解法”这一章。

回顾

首先,我想先从“逐步生成结果”开始讲起。

刚开始学C语言的时候,我们会学“斐波那契数列”,这就是典型的“逐步生成”思想,也可以理解为迭代

我相信斐波那契大家肯定很熟悉了,这里就不在介绍了。

解决简单情况下的问题:上楼梯

题意:有一个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶,2阶,3阶。

请实现一个方法,计算小孩有多少种上楼梯的方式。

为了防止溢出,请将结果Mod1000000007。 给定一个正整数,请返回一个数,代表上楼的方式数。保证n小于等于100000.

由题意,我们可以做出如下图所示的推理:


递推:

#include<stdio.h>
#define mod 1000000007
int main(){
  int x1 = 1, x2 = 2, x3 = 4, n;
  long long ans = 0;
  scanf("%d",&n);
  if(n < 0) ans = 0;
  else if(n == 0 || n == 1) ans = x1;
  else if(n == 2) ans = x2;
  else if(n == 3) ans = x3;
  for(int i = 4; i <= n; i++){
    int t = x1;
    x1 = x2;  x2 = x3;
    x3 = ((x1+x2)%mod+t)%mod;
    ans = x3;
  } 
  printf("%d\n",ans);
  return 0;
}

递归:

long f1(int n){
  if(n < 0) return 0;
  if(n == 0 || n == 1) return 1;
  if(n == 2) return 2;
  return f1(n-1)%mod + f1(n-2)%mod + f1(n-3)%mod;
}

如果大家数学基础比较好,也可以利用递推公式,直接得出封闭解。由于我数学推理不是很好,在这里就不展示了。

推广到稍微复杂的问题:机器人走方格

有一个X*Y的网格,一个机器人只能走格点且只能向右或向下走,要从左上角走到右下角。

请设计一个算法,计算机器人有多少种走法。

给定两个正整数int x,int y,请返回机器人的走法数目。

保证x+y小于等于12。
由题意我们可以推出:

  • 当x或者y为1时,走法都是1种。
  • 当x或者y为2,另一个为1时,也就是(1,2)或者(2,1),走法也都是1种。
  • 当x和y都为2时,走法是两种 ……

顺着思路往下想,我们可以画出这样一幅示意图

从这幅图我们可以发现:

在x+y>=3时,当前状态的解都为(x-1,y)状态的解和(x,y-1)的解的和
也就是f(x,y)=f(x-1,y)+f(x,y-1)
因为在任何一种状态,我们都有两种选择,下或者右,如果现在向下走,那么下一种状态的解即为(x,y-1)的解,如果向右,那么下一种状态为(x-1,y)的解。

所以,我们同样可以用递推或者递归来写代码~

递推:

建立一个二维数组存放每一个状态对应的解,逐步迭代得到所有解

#include<stdio.h>
int solve1(int x, int y){
  int a[x+1][y+1];
  for(int i = 1; i <= x; i++) a[i][1] = 1;//当x或y为1时,解法都是一种
  for(int i = i; i <= y; i++) a[1][i] = 1;
  for(int i = 2; i <= x; i++){
    for(int j = 2; j <= y; j++){
      a[i][j] = a[i-1][j]+a[i][j-1];  //利用递推关系迭代出所有的解
    }
  }
  return a[x][y];
} 
int main(){
  int x,y;
  scanf("%d%d",&x,&y);
  printf("%d\n",solve1(x,y));
  return 0;
}

递归:

int solve2(int x, int y){
  if(x == 0 || y == 0 || x == 1 || y == 1) return 1;
  return solve2(x-1,y)+solve2(x,y-1);
}

由以上两道例题我们发现,递推和递归都是对于需要迭代问题的解法

区别在于:递推是正着思考正着写,递归是正着思考倒着写

递归对于这类问题的思考的锻炼是非常有帮助的。

还有一道更难一点的题:硬币表示(点击阅读我的另一篇文章)

相关文章
|
7月前
|
算法 测试技术 C++
【动态规划】【图论】【C++算法】1575统计所有可行路径
【动态规划】【图论】【C++算法】1575统计所有可行路径
|
人工智能 算法
【算法分析与设计】递归与分治策略(二)
【算法分析与设计】递归与分治策略
|
存储 人工智能 算法
【算法分析与设计】动态规划(下)(一)
【算法分析与设计】动态规划(下)
|
6月前
|
算法
【经典LeetCode算法题目专栏分类】【第4期】BFS广度优先算法:单词接龙、最小基因变化、二进制矩阵中的最短路径
【经典LeetCode算法题目专栏分类】【第4期】BFS广度优先算法:单词接龙、最小基因变化、二进制矩阵中的最短路径
|
机器学习/深度学习 算法 编译器
【算法分析与设计】递归与分治策略(一)
【算法分析与设计】递归与分治策略
|
7月前
|
人工智能 算法 BI
*算法基础:双指针算法
*算法基础:双指针算法
59 1
*算法基础:双指针算法
|
算法
广度优先遍历(BFS):逐层探索图的算法奥秘
在图论中,广度优先遍历(Breadth-First Search,BFS)是一种图遍历算法,它以一种逐层的方式探索图的节点。通过从起始节点开始,逐层向外扩展,BFS能够帮助我们解决许多与图相关的问题。
135 0
|
算法 搜索推荐 Windows
【算法分析与设计】递归与分治策略(三)
【算法分析与设计】递归与分治策略
|
消息中间件 人工智能 算法
【算法分析与设计】动态规划(下)(二)
【算法分析与设计】动态规划(下)
|
存储 算法
【算法分析与设计】动态规划(下)(三)
【算法分析与设计】动态规划(下)

热门文章

最新文章