DP一直是我的一个薄弱项,最近开始准备刷DP了,在此记录下DP的刷题记录,比较新的怪的题目可能会单独写一篇博客,比较传统的就统一记录在这一篇博客里了。
刷题暂停了,读博了
首先最简单的无疑是直接根据递推式去递归,但是递归时间复杂度和空间复杂度都很高,所以改用更好的方法,也就是DP。
看了下题解,有一个总结的很好,在此记录:
DP适用的题目:一个模型三个特征
一个模型:多阶段最优决策模型
三个特征:
- 最优子结构:后面阶段的最优解可以通过前面阶段的最优解解出;
- 无后效性:前面阶段的最优解不受后面阶段的影响;
- 重复子问题:解后面阶段最优解时会用到多次前面阶段的最优解;
斐波那契数列便满足这三个特征,并且该问题是多阶段最优决策模型。
- 斐波那契数列f(n)有n个阶段,在f(n)时,有f(1)…f(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-i)的结果不受f(n)的影响,即无后效性。
- 解f(n)时需要多次去解f(n-i),因此有重复子问题,
因此斐波那契数列可以用DP去解。
解DP问题最关键的是要找到状态转移方程,在这个问题里,状态转移方程已经告诉我们了就是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)=f(n−1)+f(n−2)
f ( n + 1 ) = f ( n ) + f ( n − 1 ) f(n+1)=f(n)+f(n-1) f(n+1)=f(n)+f(n−1)
第一,由上面两式子可知每回合状态只和前面两个回合有关,因此我们在代码里只需要保存前面两个回合的状态,以前的状态都没用,可以直接删掉,我们记作fn_1和fn_2。第二,在第n回合fn=fn_1+fn_2,然后需要去更新fn_1和fn_2,更新方式为fn_2=fn_1(也就是说下一回合的fn_2其实就是上一回合的fn_1,见上面的两个公式可以知道),fn_1 = fn(也就是说下一回合的fn_1其实就是上一回合的fn,见上面的两个公式可以知道)。第三,注意下初始状态和n是第几回合就可以了,这个比较简单,不赘言了。
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
#
# @param n int整型
# @return int整型
#
class Solution:
def Fibonacci1(self , n: int) -> int:
# write code here
if n ==1 or n == 2:
return 1
else:
return self.Fibonacci(n-1)+self.Fibonacci(n-2)
def Fibonacci(self , n: int) -> int:
fn=fn_1=fn_2=1
if n == 1 or n == 2:
return 1
for i in range(n-2):
fn = fn_1+fn_2
fn_2 = fn_1
fn_1 = fn
return fn
n=36
print(Solution().Fibonacci(n))