本文将使用动态规划解决三道题目:完全背包、零钱兑换 II、组合总和 IV。本篇报告将介绍动态规划的思路以及Java代码实现。
一、完全背包
完全背包问题是指有一个容量为V的背包和n种不同的物品,每种物品有无限个。第i种物品的体积是v[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。
动态规划的思路:
-
定义状态:设f[i][v]表示前i种物品恰好放入一个容量为v的背包可以获得的最大价值。
-
状态转移方程:f[i][v]=max{f[i-1][v-kv[i]]+kw[i]|0<=k*v[i]<=v}
-
边界条件:f[0][j]=0(0<=j<=V),f[i][0]=0(0<=i<=n)
-
最终答案:f[n][V]
Java代码实现:
public static int knapsack(int[] v, int[] w, int V) {
int n = v.length;
int[][] f = new int[n + 1][V + 1];
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= V; j++) {
int maxK = j / v[i - 1];
for (int k = 0; k <= maxK; k++) {
f[i][j] = Math.max(f[i][j], f[i - 1][j - k * v[i - 1]] + k * w[i - 1]);
}
}
}
return f[n][V];
}
二、零钱兑换 II
零钱兑换 II 是一道非常经典的动态规划问题,题目描述如下:给定不同面额的硬币和一个总金额,编写一个函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。
动态规划的思路:
-
定义状态:设f[i][j]表示前i种硬币凑成j元的方案数。
-
状态转移方程:f[i][j]=f[i-1][j]+f[i][j-coins[i-1]] (j>=coins[i-1])
-
边界条件:f[0][j]=0(0<=j<=amount),f[i][0]=1(0<=i<=n)
-
最终答案:f[n][amount]
Java代码实现:
public static int change(int amount, int[] coins) {
int n = coins.length;
int[][] f = new int[n + 1][amount + 1];
for (int i = 0; i <= n; i++) {
f[i][0] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= amount; j++) {
f[i][j] = f[i - 1][j];
if (j >= coins[i - 1]) {
f[i][j] += f[i][j - coins[i - 1]];
}
}
}
return f[n][amount];
}
三、组合总和 IV
组合总和 IV 是一道非常经典的动态规划问题,题目描述如下:给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。
动态规划的思路:
-
定义状态:设f[i]表示凑成i元的方案数。
-
状态转移方程:f[i]+=f[i-nums[j]] (i>=nums[j])
-
边界条件:f[0]=1
-
最终答案:f[target]
Java代码实现:
public static int combinationSum4(int[] nums, int target) {
int[] f = new int[target + 1];
f[0] = 1;
for (int i = 1; i <= target; i++) {
for (int j = 0; j < nums.length; j++) {
if (i >= nums[j]) {
f[i] += f[i - nums[j]];
}
}
}
return f[target];
}
以上就是三道题目的动态规划思路以及Java代码实现。