动态规划问题(一)最大切分段数
问题描述
给你一段长为 L 的木棒,现在有三种切分长度 p、q、r,你只能将这根木棒切成在这三种长度内的组合,求这根木棒最大能够切成多少段。
比如说:现在一根长为 11 的木棒,可以切分的长度为 2、3、5,因此该木棒最多能够被切成 5 段,分别是:{2、2、2、2、3}
解决思路
由于只能在候选的三个长度内进行组合,因此可以考虑将这根木棒递归地按照候选长度进行切分,然后得到最终的组合数。然而,在这里使用递推地方式可能会更好一些。
解决方案:在每个长度位置检测是否能够加上对应的候选长度,如果能加上,那么就是能切分的段数加一,但是可能也会有其它的方案可以组合目标长度,因此需要对比得到最大值。
状态转换方程:
\[f(n) = max(f(n - p), f(n - q), f(n - r)) + 1
\]
边界分析:对于长度为 0 的木棒,不存在相对应的切分方法,因此它的组合数为 0。
实现
public class Solution {
/**
* 得到长度为 n 的木棒能够按照候选的长度组合的最大元素个数
*
* @param n : 木棒的长度
* @param x : 可切成的候选长度 p
* @param y : 可切成的候选长度 q
* @param z : 可切成的候选长度 r
* @return : 能够切分的最大段数
*/
public static int maximizeCuts(int n, int x, int y, int z) {
int[] dp = new int[n + 1];
// 由于有的长度是无法在当前的候选长度集合中进行切分的,因此需要考虑跳过它
for (int i = 1; i <= n; ++i)
dp[i] = -1;
// 边界情况,长度为 0 的木棒对任何候选切分子集切分的段数都为 0
dp[0] = 0;
for (int i = 0; i <= n; ++i) {
// 如果当前访问的长度是无法由现有的组合子集组合,那那么就说明不能被切分,跳过它
if (dp[i] == -1) continue;
/*
如果当前的长度加上备选的切割长度要小于总共的木棒长度,
那么就可以将组个分割的子段长度加到现有的长度上,同时增加段数
由于加上的长度结果可能由多种组合情况,因此要找到最大的组合段数
*/
if (i + x <= n)
dp[i + x] = Math.max(dp[i + x], dp[i] + 1);
if (i + y <= n)
dp[i + y] = Math.max(dp[i + y], dp[i] + 1);
if (i + z <= n)
dp[i + z] = Math.max(dp[i + z], dp[i] + 1);
}
// 如果目标段数不能由现有的组合段数组合而来,则返回 0
if (dp[n] == -1) return 0;
return dp[n];
}
}