动态规划
简介
https://www.cnblogs.com/loveer/p/11786100.html
动态规划遵循一套固定的流程:递归的暴力解法( O(2^n) ) -> 带备忘录的递归解法( O(n) ) -> 非递归的动态规划解法( O(n) )。
「自顶向下」:
是从上向下延伸,
都是从一个规模较大的原问题比如说 f(20),向下逐渐分解规模,直到 f(1) 和 f(2) 触底,然后逐层返回答案。
「自底向上」:
直接从最底下,最简单,问题规模最小的 f(1) 和 f(2) 开始往上推,直到推到我们想要的答案 f(20),
解法:
1. 将原问题分解为子问题
f(0),f(1),f(2)...f(n)
2. 确定状态
f(n)
3. 确定一些初始状态(边界条件)的值
f(n)=0
4. 确定状态转移方程(当前子问题值与前一个子问题值的关系)
f(n) 是一个状态 n,这个状态 n 是由状态 n - 1 和状态 n - 2 相加转移而来,这就是状态转移
动态规划问题最困难的就是写出状态转移方程。
适合使用动规求解的问题
1. 问题具有最优子结构(问题的最优解所包含的子问题的解也是最优的)
2. 求最优解问题
动态规划问题
1. 0-1背包问题
①、将原问题分解为子问题(子问题和原问题形式相同,且子问题解求出就会被保存);
②、确定状态:0-1背包中一个状态就是N个物体中第i个是否放入体积为V背包中;
③、确定一些初始状态(边界状态)的值;
④、确定状态转移方程,如何从一个或多个已知状态求出另一个未知状态的值。(递推型)
案例:
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
示例2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.
Python
class Solution:
def canPartition(self, nums: List[int]) -> bool:
# 0-1 背包问题
sums=sum(nums)
if sums%2!=0:return False
else:avg=int(sums/2)
B = [[False for _ in range(avg + 1)] for _ in range(len(nums))]
for i in range(avg + 1):
B[0][i] = False if nums[0] != i else True
for i in range(1,len(nums)):
for j in range(1,avg+1):
if j<nums[i]:
B[i][j]=B[i-1][j]
else:
B[i][j]=B[i-1][j] or B[i-1][j-nums[i]]
return B[-1][-1]