leetcode 474.一和零

1. 思路

把总共的0和1的个数视为背包的容量,每一个字符串视为装进背包的物品。这道题就可以使用0-1背包问题的思路完成,这里的目标值是能放进背包的字符串的数量。
动态规划的思路是:物品一个一个尝试,容量一点一点尝试,每个物品分类讨论的标准是:选与不选。
定义状态:背包问题 - 图1表示输入字符串在子区间背包问题 - 图2能够使用背包问题 - 图3个0和背包问题 - 图4个l的字符串的最大数量
状态转移方程:背包问题 - 图5
初始化:为了避免分类讨论,通常多设置一行。这里可以认为,第 0 个字符串是空串。第 0 行默认初始化为 0。
输出:输出是最后一个状态,即:背包问题 - 图6

2. 代码

  1. public class FindMaxForm1 {
  2. public int findMaxForm(String[] strs, int m, int n) {
  3. int length = strs.length;
  4. int[][][] dp = new int[length+1][m+1][n+1];
  5. for(int i=1;i<=length;i++){
  6. int[] zerosOnes = getZerosOnes(strs[i-1]);
  7. int zeros = zerosOnes[0],ones = zerosOnes[1];
  8. for(int j=0;j<=m;j++){
  9. for(int k=0;k<=n;k++){
  10. dp[i][j][k] = dp[i-1][j][k];
  11. if(j>=zeros&&k>=ones){
  12. dp[i][j][k] = Math.max(dp[i][j][k],dp[i-1][j-zeros][k-ones]+1);
  13. }
  14. }
  15. }
  16. }
  17. return dp[length][m][n];
  18. }
  19. public int[] getZerosOnes(String str){
  20. int[] zerosOnes = new int[2];
  21. int length = str.length();
  22. for(int i=0;i<length;i++){
  23. zerosOnes[str.charAt(i)-'0']++;
  24. }
  25. return zerosOnes;
  26. }
  27. public static void main(String[] args) {
  28. System.out.println(new FindMaxForm1().findMaxForm(new String[]{"10", "0001", "111001", "1", "0"},5,3));
  29. }
  30. }

Leetcode 1049.最后一块石头的重量II

1. 思路

问题转换彻底切换为01背包问题:从stones数组中选择,凑成总和不超过背包问题 - 图7的最大价值。定义背包问题 - 图8代表考虑前背包问题 - 图9个物品(数值),凑成不超过背包问题 - 图10的最大价值。
转移方程:
背包问题 - 图11

2. 代码

  1. //01背包问题
  2. public int lastStoneWeightII(int[] stones) {
  3. int sum = 0;
  4. for(int stone:stones){
  5. sum+=stone;
  6. }
  7. int target = sum/2;
  8. int[] packages = new int[target+1];
  9. for(int i=0;i<stones.length;i++){
  10. int cur = stones[i];
  11. for(int j = cur;j<=target;j++){
  12. packages[j] = Math.max(packages[j],packages[j-cur]+cur);
  13. }
  14. }
  15. return sum-packages[target]*2;
  16. }

Leetcode 518.零钱兑换

1. 思路

通过动态规划的方法计算可能的组合数。用背包问题 - 图12表示金额之和等于背包问题 - 图13的硬币组合数,目标是求背包问题 - 图14。动态规划的边界是背包问题 - 图15。不选取任何硬币时,金额之和为0,因此只有1种硬币组合。
对于面额为背包问题 - 图16的硬币当背包问题 - 图17时,如果存在一种硬币组合的金额之和等于背包问题 - 图18,则在该硬币组合中增加一个面额为背包问题 - 图19的硬币,即可得到一种金额之和等于i的硬币组合。因此需要遍历背包问题 - 图20,对于其中的每一种面额的硬币,更新数组dp 大于等于该面额的元素的值。
由此可以得到初始动态规划的做法:

  • 初始化背包问题 - 图21
  • 遍历背包问题 - 图22,对于其中的每个元素coin,进行如下操作:
    • 遍历i从coin到amount,将背包问题 - 图23的值加到背包问题 - 图24
  • 最终得到背包问题 - 图25的值

    2. 代码

    1. class Solution {
    2. public int change(int cnt, int[] cs) {
    3. int n = cs.length;
    4. int[] f = new int[cnt + 1];
    5. f[0] = 1;
    6. for (int i = 1; i <= n; i++) {
    7. int val = cs[i - 1];
    8. for (int j = val; j <= cnt; j++) {
    9. f[j] += f[j - val];
    10. }
    11. }
    12. return f[cnt];
    13. }
    14. }

    Leetcode 279.完全平方数

    1. 思路

    1. 思路
  • 首先初始化长度为背包问题 - 图26的数组背包问题 - 图27,每个位置都为0
  • 如果n为0,则结果为0
  • 对数组进行遍历,下标为背包问题 - 图28,每次都将当前数字先更新为最大的结果,即背包问题 - 图29,比如背包问题 - 图30,最坏结果为4=1+1+1+1 即为4个数字
  • 动态转移方程:背包问题 - 图31表示当前数字,j*j表示平方数
  • 时间复杂度:背包问题 - 图32,sqrt为平方根

    2. 代码

    1. public class NumSquares {
    2. public int numSquares(int n) {
    3. int target = (int)Math.sqrt(n)+1;
    4. int[] dp = new int[n+1];
    5. Arrays.fill(dp,Integer.MAX_VALUE);
    6. dp[0] = 0;
    7. for(int i=1;i<=target;i++){
    8. for(int j=i*i;j<=n;j++){
    9. dp[j] = Math.min(dp[j],dp[j-i*i]+1);
    10. }
    11. }
    12. return dp[n];
    13. }
    14. }