在学习动态规划之前,我们首先要知道什么是动态规划?
动态规划与分治方法类似,都是通过组合子问题的解来求解原问题的。再来了解一下什么是分治方法以及两者之间的差别,分治方法将问题划分为互不相交的子问题,递归的求解子问题,再将它们的解组合起来,求出原问题的解。而动态规划与之相反,动态规划应用与子问题重叠的情况,即不同的子问题具有公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题)。在这种情况下,分治方法就会做许多不必要的工作,它会反复求解那些公共子子问题。而动态规划对于每一个子子问题只求解一次,将其解保存在一个表格里面,从而无需每次求解子子问题时都重新计算,避免了不必要的计算工作。
动态规划的方法一般用来求解最优化问题。这类问题可以有很多可行解,每个解都有一个值。我们希望找到具有最优值的解,我们称这样的解为问题的一个最优解而不是最优解,因为可能有多个解都达到最优值。
解决动态规划问题一般分为四步:
1、定义一个状态,这是一个最优解的结构特征
2、进行状态递推,得到递推公式
3、进行初始化
4、返回结果
我们通过几个例子来演示动态规划的过程
[编程题]斐波那契数列
热度指数:374145时间限制:1秒空间限制:32768K
算法知识视频讲解
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39
这个题目相信大家都很熟悉:
在我们学习递归的时候应该都见过:我们先写一个递归版本的。
代码如下:
class Solution {
public:
int Fibonacci(int n) {
if(n<=0)
return 0;
if(n == 1 || n == 2)
return 1;
return Fibonacci(n-1)+Fibonacci(n-2);
}
};
这个代码的时间复杂度是2^n,也就是指数递增,如果求一个很大数字的时候,运行就会出错,造成栈溢出。
下面我们用动态规划来求解这个问题:
def fabonacii_fun(n):
if n <= 0:
return 0
if n == 1:
return 1
fn1 = 0
fn2 = 1
result = 0
for i in range(2, n + 1):
result = fn1 + fn2
fn1 = fn2
fn2 = result
return result
if __name__ == "__main__":
print(fabonacii_fun(6))
可以对这个代码进行优化,将空间复杂度变为O(1)
[编程题]变态跳台阶
热度指数:221924时间限制:1秒空间限制:32768K
算法知识视频讲解
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
先来看简单分析:
def jumpfloor_fun(n):
if n == 0:
return 1
if n == 1:
return 1
total = 1
for i in range(2, n +1):
total = 2 * total
return total
if __name__ =="__main__":
print(jumpfloor_fun(3))
def rectCover_fun(n):
if n== 0:
return 0
if n==1:
return 1
if n ==2:
return 2
fn1 = 1
fn2 =2
total = 0
for i in range(3,n +1):
total = fn1 + fn2
fn1 = fn2
fn2 = total
return total
print(rectCover_fun(4))
编程题]最大连续数列和
热度指数:5518时间限制:3秒空间限制:32768K
算法知识视频讲解
对于一个有正有负的整数数组,请找出总和最大的连续数列。
给定一个int数组A和数组大小n,请返回最大的连续数列的和。保证n的大小小于等于3000。
测试样例:
[1,2,3,-6,1]
返回:6
分析:
状态:f(i)以array[i]结尾的连续子序列的最大和
状态递推:
f(i) = max(f(i-1) + array[i],array[i])
初始化: f(0) = array[0]
返回结果:max(f)
import numpy as np
def getMaxSum_fun(A,n):
if n == 0:
return 0
f = np.zeros(n)
f[0] = A[0]
for i in range(1, n):
f[i] = max(f[i-1] + A[i], A[i])
return int(max(f))
if __name__ == "__main__":
print(getMaxSum_fun([6,-3,7,-15,1,22], 6))
[编程题]word-break
给定字符串s和单词词典.,确定s是否可以分割为一个或多个字典单词的空分序列。
例如,给定
s =”leetcode”,
dict =[“leet”, “code”].
返回true,因为“leetcode”可以被分割为“leet code”。
分析:
import numpy as np
def workbreak_fun(s, dicts):
n = len(s)
if len(s) == 0 or len(dicts) == 0:
return False
wordchack = np.zeros(n+1)
wordchack[0] = True
for i in range(1, len(s)+1):
for j in range(0, i):
if wordchack[j] and s[j:i] in dicts:
wordchack[i] = True
break
return bool(wordchack[n])
strlist = "cars"
dictlist = ["car", "ca", "rs"]
print(workbreak_fun(strlist, dictlist))
[编程题]triangle
给定一个三角形,从上到下求最小路径和。每一步你可以移动到下面行的相邻数字。
例如,给定以下三角形
[
〔2〕;
[3,4],
[6],[5],[7]
[41,1,3]
]
从上到下的最小路径和是11(即2+3+5+1=11)。
注:
如果你只能使用O(n)额外的空间来做这一点,其中n是三角形中的总行数。
分析:
def minpathtotal_fun(A):
if len(A) == 0 :
return 0
min_sum= A
for i in range(1, len(A)):
for j in range(i+1):
# 在最左边
if j == 0:
min_sum[i][j] = min_sum[i-1][j] + A[i][j]
# 最右边
elif j == i:
min_sum[i][j] = min_sum[i - 1][j-1] + A[i][j]
# 中间
else:
min_sum[i][j] = min(min_sum[i - 1][j - 1], min_sum[i - 1][j]) + A[i][j]
return min(min_sum[len(A) - 1])
num_list = [
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
print(minpathtotal_fun(num_list))