动态规划理论基础

动规五部曲:

1.确定dp数组及其下标含义

2.确定递推公式

3.dp数组如何初始化

4.确定遍历顺序

5.举例推导dp数组

斐波那契数列

状态转移方程:dp[i] = dp[i-1] + dp[i-2]

爬楼梯

状态转移方程:dp[i] = dp[i-1] + dp[i-2]

初始化dp数组的时候,dp[0]的初始化其实是没有含义的。

正确的思路是:dp[1] = 1,dp[2]=2,从3开始遍历。

使用最小花费爬楼梯

推导现在的状态怎么出现的:(爬上当前楼梯不需要花费,在前一阶已经花了)

状态转移方程:dp[i] = min(dp[[i-1]+costs[i] , dp[i-2] + costs[i-2])

不同路径

从出发点到终点有几种路径。

dp[i] [j] = 从左边+从上面来的路径 = dp[i-1] [j] + dp[i] [j-1]

需要注意数组初始化!最左、最上的边框都需要初始化为1,因为只有一条路径能都到达。

不同路径(添加障碍版)

从出发点到终点有几种路径。

( i , j )没有障碍时)dp[i] [j] = 从左边+从上面来的路径 = dp[i-1] [j] + dp[i] [j-1]

需要注意数组初始化!最左、最上的边框(在障碍之前的)都需要初始化为1,因为只有一条路径能都到达,被障碍挡住的路径不可达

整数拆分

将数字 i 拆成两种:j*(i-j) / j *dp[i-j]

类似于2 : 1*(2-1) / 1 *dp[2-1]

以及需要选取乘积最大化:需要循环比较

因此递推公式:dp[j] = max( dp[j] , max(j(i-j) , j * dp[i-j]) )*

不同的二叉搜索树

给出n个不同的节点求能组成多少个不同的二叉搜索树

2为节点的时候有两个不同的二叉树:1为头结点,2为右子树 、2为头结点,1为左子树

*dp[i] += dp[i-j]dp[j-1] (1为头结点时,左子树有0个元素,右子树有2个元素 )

01背包理论基础

二维数组

1.确定dp数组及其下标含义

dp[i] [j]:表示从下标为[ 0 - i ]的物品里任意取,放进容量为j的背包,价值总和最大是多少

2.确定递推公式

dp[i] [j] = max(dp[i-1] [j] , dp[i-1] [j-weight[i]]+values[i])

3.数组初始化

按照题意来。非零下标,如果题意给的都是正整数则初始化为0;求最小值则初始化为最大值(Math.MaxInt)

4.确定遍历顺序

01背包二维dp数组在遍历顺序上,无论是外层物品、里层背包或外层背包、里层物品都是可以的。

5.举例推导。。

一维数组

和二维数组的区别:背包的状态是可以压缩的 =》压缩为一维数组

二维数组定义:dp[i] [j]:表示从下标为[ 0 - i ]的物品里任意取,放进容量为j的背包,价值总和最大是多少

1.确定dp数组及其下标含义

dp[j]:容量为 j 的背包,所背的物品价值最大为dp[j]

2.一维dp数组的递推公式

dp[j] = max(dp[j],dp[j - weight[i]] + values[i])

3.数组初始化

如果物品价值都是大于0的,dp数组初始化的时候初始化为0即可。

4.一维dp数组遍历顺序

外层为物品,内层为背包且倒序遍历。

5.举例推导dp数组。。

分割等和子集

将总和sum分为一半,即target = sum/2为背包的最大容量,元素的数值为物品

dp[j] = max(dp[j],dp[j-nums[i]] + nums[i])

如果dp[target] == target ,说明集合中的子集总和正好可以凑成总和 j。

最后一块石头的重量

和上题类似、将石头尽可能分成重量相同的两份,然后sum - 2*dp[target] 就好啦

目标和

要求在数列之间加入+或-,使其和为target,有多少种组合。

本题我们通过求出target、sum、加法集合、减法集合的等式:left = (target + sum)/2

求组合数量 =》累加 ,因为累加需要数组初始化时不为0dp[0]说明数组里的元素0前面无论是放加法还是减法都属于一种方法,因此dp[0] = 0

dp[j] += dp[j - nums[i]]

一维数组、物品外循环,背包内循环且倒序

一和零

数组里面有任意个由0和1组成的字符串

关键词:0和1的数量有限,因此我们应该使用01背包。把0和1 当作两个维度的背包,str当作物品

求出0和1的数量为多少

递推公式:dp[i] [j] = max(dp[i] [j] ,dp[i-zeroNum] [j-oneNum] +1 )

i 和 j 的范围为zeroNum - m ,oneNum - n ,且需要倒序遍历!(背包维度)

完全背包理论

完全背包和01背包区别在于:完全背包的物品数量是无限。

完全背包的物品可以添加多次,因此遍历背包容量要从小到大遍历。

排列数:背包在外循环遍历,物品在内循环遍历

组合数(去重):物品在外循环遍历,背包在内循环正序遍历 – 保证了物品只被遍历一次

零钱兑换II

求一共有多少种凑成总金额的组合数,要求去重,因此物品(coins)在外循环,背包(amount)在内循环。

推导公式:dp[j] += dp[j - i]

i : 1 - coins.length / j : coin[i] - amont

组合总和IV

求排列数,因此背包(target)在外循环,物品(nums)在内循环

推导公式:dp[i] += dp[i- nums[j]]

爬楼梯

求总数,使用累加。

背包(n阶)在外循环,物品(m层跳跃)在内循环

推导公式:dp[i] += dp[i-j]

零钱兑换

求所需最少,且数量无限,完全背包。

数组初始化为最大值,以免数值被初始值覆盖。

背包(amount)在外循环,物品(coins)在内循环。

dp[i] = min(dp[i-coins[j]] +1 , dp[i])

完全平方数

将完全平方数看作物品!

dp[j] = min(dp[j] , dp[j - i*i] + 1)

要记得dp[j - weight[i]] + values[i]


写了五小时终于写完了总结XD。。。