动态规划java版本
模版
1、确定 base case,这个很简单,基础目标赋值;
2、确定「状态」,也就是原问题和子问题中会变化的变量。所以唯一的「状态」就是目标变量
3、确定「选择」,也就是导致「状态」产生变化的行为。目标如何变化,就是按照什么规则变化就是你的「选择」。
4、明确 dp 函数/数组的定义。我们这里讲的是自顶向下的解法,所以会有一个递归的 dp 函数,一般来说函数的参数就是状态转移中会变化的量,也就是上面说到的「状态」;函数的返回值就是题目要求我们计算的量。
# 初始化 base case
dp[0][0][...] = base
# 进行状态转移
for 状态1 in 状态1的所有取值:
for 状态2 in 状态2的所有取值:
for ...
dp[状态1][状态2][...] = 求最值(选择1,选择2...)
案例1
剑指 Offer 14- I. 剪绳子
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
提示:
2 <= n <= 58
套入模版
class Solution {
public int cuttingRope(int n) {
//为什么要这么写呢,是因为如果写成new int[n]的话,包含项0-n-1,而我们所求要直接到n所以写成new int[n+1]表示0-n;
int[] dp = new int[n+1];
//base case 确定base
dp[2]=1;
//从三开始,因为要从二开始切,从一开始切对乘积没有贡献
for(int i=3;i<n+1;i++){
for(int j=2;j<i;j++){
//状态转移方程
// 剪了第一段后,剩下(i - j)长度可以剪也可以不剪。如果不剪的话长度乘积即为j * (i - j);如果剪的话长度乘积即为j * dp[i - j]。取两者最大值max(j * (i - j), j * dp[i - j])
dp[i] = Math.max(dp[i],Math.max(j*(i-j),j*dp[i-j]));
}
}
return dp[n];
}
}
总结
状态转移公式确定很难很难,多刷多做!!!