弱校讲课,大牛退散
简单递归+DFS入门
这里介绍,递归,深度优先搜索,的简单入门,这两个概念在下面都会提到。
递归:就是函数自身调用自身的过程,举个例子
我的递归思路,代码风格
最前面写结束条件
下面写递归操作
1 递归写阶乘
int f(int n)
{
if(n == 0) return 1; // 0的阶乘为1
return n*f(n-1);
}
我们模拟一下这个过程。
时间复杂度分析
2 爬台阶:每步只能走一或两个台阶,求从第1层 爬到 第n层台阶共有多少种走法。
首先我们正着想,f[N]表示走到第n层的方案数
n == 1, f[1] = 1
n == 2, f[2] = 1
n > 2,f[n] = f[n-1] + f[n-2],走到n-2层走两层 + 走到n-1层走一层。
不难发现其实就是斐波那契数列
换种方法,我们再反着想,
f[n] 只可能从第f[n-1] 和 f[n-2] 走来,所以求f[n] 即求 f[n-1],f[n-2]的和
f[n] = f[n-1] + f[n-2] = f[n-2]+f[n-3]+f[n-3]+f[n-4] = …………一直到f[1] 时我们知道解为1,f[2] = 1。
(思考结束条件为什么需要两个,结束条件能不能写其他数量个)
我们可以尝试着实现下这个反向的过程
int f(int n) // 此代码需保证 n 为正整数
{
if(n == 1 || n == 2) return 1;
return f(n-1) + f(n-2);
}
我们在模拟一下这个过程。
时间复杂度分析
开个数组优化,这里带了点记忆化搜索的感觉(这个不属于今天讲的范围,但是可以作为提高要求自行提前了解)
#include <stdio.h>
int dp[50]; // 记忆化,记忆化有点DP的感觉所以我选择取名为DP
int f(int n)
{
if(dp[n]) return dp[n];
return dp[n] = f(n-1) + f(n-2);
}
int main()
{
dp[1] = 1;
dp[2] = 1;
printf("%d\n",f(20));
return 0;
}
时间复杂度分析
3 递归写10层for循环嵌套代码如下
for(int i = 1; i <= 5; ++i)
{
for(int j = 1; j <= 5; ++j)
{
for(int k = 1; k <= 5; ++k)
{
………………
}
}
}
改成递归形式,第一次调用传个10即10层for循环嵌套,可见递归是非常的灵活
void wtf(int step)
{
if(step == 0) return;
for(int i = 1; i <= 5; ++i)
{
wtf(step-1);
}
}
递归与栈的关系 括号配对
DFS(深搜):不撞南墙不回头
画个迷宫1表示墙,0表示路,,问左上角到右下角是否存在路径
0, 1, 0, 0, 0
0, 1, 1, 1, 0
0, 0, 0, 0, 0
0, 1, 1, 1, 0
0, 0, 0, 1, 0
我们可以傻傻的按照
if(能走到一个没走到的点)到那个点;
else 回到过来的点;
按下左上右顺序模拟下
#include <stdio.h>
#include <string.h>
// 可以借助全局变量表示状态
int flag = 0; // 0没找到,1找到
int mov[4][2] = {0,1,1,0,0,-1,-1,0}; // 其实就是四个方向,自己可以检验
int mp[10][10] = /*可以自行修改图形实验*/
{
{0, 1, 0, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 0, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 0, 1, 0},
};
// map缩写,存图
int vis[20][20]; // 避免来回走死循环,而且某些情况可以优化下面会讲
void dfs(int x, int y)
{
if(x == 4 && y == 4)
{
flag = 1;
return;
}
for(int i = 0; i < 4; ++i)
{
int xx = x+mov[i][0];
int yy = y+mov[i][1];
// 如果越界,或者这个点来过,或者这点是墙 则跳过
// 先判断越界也很关键,或者的前面已经为真后面不会执行(因为执行不执行都是真),
// 同理&&前面为假,后一半则不执行(因为执行不执行都是假)
// 先判越界避免了vis和mp访问越界如-1(会运行时错误)
if(xx < 0 || yy < 0 || xx > 4 || yy > 4 || vis[xx][yy] || mp[xx][yy]) continue;
vis[xx][yy] = 1;
dfs(xx,yy);
if(flag) return; // 如果找到则返回,这里加不加的区别
// vis[xx][yy] = 0; // 这里加不加的区别
}
}
int main()
{
//多组输入记得初始化vis
vis[0][0] = 1;
dfs(0,0);
printf("%d\n",flag);
return 0;
}
时间复杂度分析
不开vis,会死循环
vis只在递归标记的情况,(更快的寻找
vis回溯时再取消标记的情况,(寻找多条
不用vis可以怎么修改?
题目
HDU 2041 楼梯
nyoj 2 数据水了,没有最后判断栈是否为空的数据 (((() 这个数据自己过下
nyoj 32(需要知道字典序,一个小概念)
HDU 1016 剪枝
HDU 1241 这个及下面都是图的深搜
POJ 3984 需要路径输出,带点回溯操作
HDU 1010 回溯带点操作,剪枝优化
有意思的操作
递归求GCD,(代码一行比较精简)
int gcd(int a, int b)
{
return a%b ? gcd(b, a%b) : b;
}