dp整理

86 阅读3分钟

1.思想

自底向上、空间换时间——求解多阶段决策问题

  • 状态定义:dp[i]爬到第i层的方法数
  • 状态转移方程:dp[i] = dp[i-1]+dp[i-2]
  • 初始化:dp[1]=1 dp[2]=2
  • 输出:dp[n]

2.三个特性

重复子问题
最优子结构:规模更大的可以由小的最优解得出
无后效性:已经求出的子问题不受更大规模问题的影响

2.1 重复子问题

leetcode91

public int numDecodings(String s) {
            //重复子问题
            //1.状态定义dp[i],以位置i结尾有多少种组合方式
            //2.状态转移方程dp[i] = dp[i-1]*s.substring(i, i + 1)是否合法 + dp[i-2]*s.substring(i-1, i + 1)是否合法
            //3.初始化 dp[1] dp[2]
            //4.输出dp[s.length]
            int[] dp = new int[s.length()];
            if (s.length() <= 1){
                return illeagal(s.substring(0, 1)) ? 0 : 1;
            }
            dp[0] = illeagal(s.substring(0, 1)) ? 0 : 1;
            dp[1] = dp[0]*(illeagal(s.substring(1, 2)) ? 0 : 1)
                    + (illeagal(s.substring(0, 2)) ? 0 : 1);
            if (s.length() <= 2){
                return dp[1];
            }
            for (int i = 2; i < s.length(); i++) {
                //dp[i-1]*s.substring(i, i + 1)是否合法+dp[i-2]*s.substring(i-1, i + 1)是否合法
                dp[i] = dp[i - 1] * (illeagal(s.substring(i, i + 1)) ? 0 : 1)
                        + dp[i - 2] * (illeagal(s.substring(i - 1, i + 1)) ? 0 : 1);
            }
            return dp[dp.length - 1];
        }

        public boolean illeagal(String s) {
            if (s.charAt(0) == '0')
                return true;
            if (s.length() == 1 && ((Integer.parseInt(s) < 1 || Integer.parseInt(s) > 9)))
                return true;
            if (s.length() == 2 && ((Integer.parseInt(s) > 26)))
                return true;
            return false;
        }

2.2 最优子结构

最优子结果--整体最优结果 零钱兑换

  • 定义状态:dp[i]为凑i的最少硬币数
  • 状态转移公式:dp[11] = min(dp[10]+1,dp[9]+1,dp[6]+1),即三种选择找一个最优解。

dp[amount] = min(dp[amount - coin[i]] + 1) for i in [0, len - 1] if coin[i] <= amount

  • 初始值:假设凑不出来,初始化为一个不可能的数
  • 输出dp[i]
public int coinChange(int[] coins, int amount) {
            if (amount == 0)
                return 0;
            int[] dp = new int[amount + 1];
            //都初始化为-1
            Arrays.fill(dp, amount + 1);
            dp[0] = 0;
            //已经确定的硬币为1
            for (int i = 0; i < coins.length; i++) {
                if (coins[i] <= amount)
                    dp[coins[i]] = 1;
            }
            for (int i = 1; i < dp.length; i++) {
                for (int j = 0; j < coins.length; j++) {
                //可达性
                    if (coins[j] <= i && (dp[i- coins[j]] != amount+1))
                        dp[i] = Math.min(dp[i- coins[j]] + 1, dp[i]);
                }
            }
            return dp[amount] == amount+1 ? -1 : dp[amount];

        }

2.3 无后效性

已经计算的子问题结果不会被改变 62 不同路径 way(i, j) = ways(i - 1, j) * 1 + ways(i, j - 1) * 1

重点在初始化上

public int uniquePaths(int m, int n) {
            int row = m;
            int col = n;
            int[][] dp = new int[row][col];
            //
            for (int i = 0; i < row; i++) {
                dp[i][0] = 1;
            }
            for (int i = 0; i < col; i++) {
                dp[0][i] = 1;
            }

            for (int i = 1; i < row; i++) {
                for (int j = 1; j < col; j++) {
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                }
            }
            return dp[row - 1][col - 1];
        }

打家劫舍 更加确切得状态定义 倒推,i位置偷条件下得最值和i位置不偷条件下得最值

 public int rob(int[] nums) {
        int len = nums.length;
        if (len == 0) {
            return 0;
        }
        if (len == 1) {
            return nums[0];
        }

        // dp[i][0]:考虑区间 [0..i] ,并且下标为 i 的这个房屋不偷
        // dp[i][1]:考虑区间 [0..i] ,并且下标为 i 的这个房屋偷
        int[][] dp = new int[len][2];
        // 初始化
        dp[0][0] = 0;
        dp[0][1] = nums[0];
        // 递推开始
        for (int i = 1; i < len; i++) {
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]);
            dp[i][1] = dp[i - 1][0] + nums[i];
        }
        return Math.max(dp[len - 1][0], dp[len - 1][1]);
    }

作者:liweiwei1419
链接:<https://leetcode.cn/leetbook/read/learning-algorithms-with-leetcode/9wmq9r/>
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。