【牛客】动态规划入门
1. 斐波那契数列
这似乎是一个老生常谈的题目
在古老的年代,天地间诞生了一个会自己增长的数列。大地赐予它生命,告诉它1为万物之始。天空则传授了它数列增长之道,告诉它永远以前一刻的自己为基石,方可不断超越自我,实现数列之永恒。于是这个数列就这样开始了它艰辛的增长之路,1,1,2,3,5,8,13,21,34 ... 功夫不负有心人,终于有一天,一个数学家发现了它,将它命名为Fibonacci数列,并将这个数列的传奇故事介绍给了世人。
所以,在座的程序员们,第n个数字是几呢,请写出你的代码 -_-
当我看到这个题目,第一反应是,就这?一个递归不就搞定了
public: int Fibonacci(int n) { if(n==1||n==2){ return 1; }else{ return Fibonacci(n-1)+Fibonacci(n-2); } } };
在牛客上一运行,确实通过了所有测试用例,但是运行时间也很吓人,237ms !好家伙 !
这时我想起来我貌似选的是动态规划练习题...好吧,我不会动态规划啊,于是首先去百度了下动态规划。再回到这个问题上来,上面这种简单递归的写法,有两个问题:
1)存在太多的重复计算,比如n=5,f(5)=f(4)+f(3),显然这里要算一次f(3),然后f(4)=f(3)+f(2),这里又要算一次f(3),而类似的重复计算还有很多。n越大,整个递归树就越大,重复计算就越多。
2)会出现递归的栈溢出,因为函数的调用是通过栈实现的。
这里加一嘴,请原谅我的偏题:如果编程语言对尾递归有优化,我们将递归写成尾递归可以避免栈溢出。什么是尾递归呢,就是在函数返回的时候,调用函数本身,并且return语句不能包含表达式。所以...很遗憾的是java和python语言都未对尾递归做优化。但是python的话可以使用@tail_call_optimized这个装饰器实现尾递归优化。另外再看看c++,我们只需在gcc后面加上-O2参数,就可以实现尾递归优化。
那么如何使用动态规划计算斐波那契数列呢,这是我的代码
public int Fibonacci(int n) { if(n==1||n==2) return 1; int dp[] = new int[n]; dp[0] = 1;//dp[i],i=n-1 dp[1] = 1; for(int i=2;i<n;i++){ dp[i] = dp[i-1]+dp[i-2]; } return dp[n-1]; }
大致就是,之前用递归,是一个自顶向下的过程,比如n=5,我们是从5到1去推进。而这里是一个自底向上的过程,先计算n=1,n=2 ... 保存到数组里,然后自然而然地算出n=5的情况,很显然这样有效避免了重复计算,也不需要使用递归。
2. 跳台阶
再做一个简单的动态规划题巩固一下
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法(先后次序不同算不同的结果)。1<=n<40
以下是简单的java解法
public int jumpFloor(int target) { int[] dp = new int[target+1]; dp[0]=1; dp[1]=2; for(int i=2;i<target;i++){ dp[i]=dp[i-2]+dp[i-1]; } return dp[target-1]; }
3. 换钱币
这个题目和跳台阶非常相似,一起做为简单入门题了
public int coinChange(int[] coins, int amount) { int max = amount + 1; int[] dp = new int[amount + 1]; Arrays.fill(dp, max); dp[0] = 0;//0元无法组合,返回0个硬币 System.out.println(Arrays.toString(dp)); //外循环从金额为1开始,自底向上,一直到需要的金额 for (int i = 1; i <= amount; i++) { for (int j = 0; j < coins.length; j++) { //假设已知dp[i]的最优组合,组合中最后一枚硬币面值为coins[j] //面值大于总金额的硬币不可能是组合成员之一,用条件语句过滤掉 //dp[i]可以通过dp[i-x]+1计算出 //但是不清楚x为哪个面值,所以通过该内循环试验出 if (coins[j] <= i) { dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1); } } } System.out.println(Arrays.toString(dp)); return dp[amount] > amount ? -1 : dp[amount]; }
好了,动态规划入门到此为止。以后有时间再继续做牛客上那几个动态规划中等难度的题吧。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构