hot100 | 十五、动态规划

1-leetcode70. 爬楼梯

注意:×

  1. 理解这里的dp[i] = dp[i-1]+dp[i-2]
  2. 到我这个台阶,可以从低一层的来,也可以从低两层的来,直接将二者相加就好了
    public int climbStairs(int n) {
        if (n<=2){
            return n;
        }
        int[] dp = new int[n+1];
        dp[0] = 0;
        dp[1] = 1;
        dp[2] = 2;
        for (int i = 3; i < dp.length; i++) {
            dp[i] = dp[i-1]+dp[i-2];
        }
        return dp[n];
    }

2-leetcode118. 杨辉三角

注意:×

  1. 要点1:List<Integer> preList = res.get(res.size()-1);
  2. 要点2:一定要注意这里是preList.size()-1; !!!!
  3. 要点3:getNextRow函数中要对List的前后手动的添加1
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> res = new ArrayList<>();
        if (numRows < 1) {
            return res;
        }
        List<Integer> first = new ArrayList<>();
        first.add(1);
        res.add(first);
        for (int i = 2; i <= numRows; i++) {
            List<Integer> preList = res.get(res.size()-1);
            res.add(getNextRow(preList));
        }
        return res;
    }

    private List<Integer> getNextRow(List<Integer> preList) {
        List<Integer> thisRow = new ArrayList<>();
        thisRow.add(1);
        for (int i = 0; i < preList.size()-1; i++) {
            thisRow.add(preList.get(i)+preList.get(i+1));
        }
        thisRow.add(1);
        return thisRow;
    }

3-leetcode198. 打家劫舍

注意:√

  1. 这题还是比较简单的dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i-1]);
  2. 已知1和2推出3
    public int rob(int[] nums) {
        int n = nums.length;
        if (n == 1){
            return nums[0];
        }
        int[] dp = new int[n+1];
        // base case
        dp[0] = 0;
        dp[1] = nums[0];
        for (int i = 2; i < n + 1; i++) {
            dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i-1]);
        }
        return dp[n];
    }

4-leetcode279. 完全平方数

注意:√

  1. 跟5-leetcode322很像,零钱是要在for循环内部遍历coins中判断当前需要的i - coin < 0
  2. 这里是第二轮遍历判断j*j<=i
  3. 注意这里的状态转移dp[i] = Math.min(dp[i], dp[i-j*j]+1);
    public int numSquares(int n) {
        int[] dp = new int[n + 1];
        Arrays.fill(dp, n + 1);
        // base case
        dp[0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j*j <= i; j++) {
                dp[i] = Math.min(dp[i], dp[i-j*j]+1);
            }
        }
        return dp[n]==n+1? -1:dp[n];
    }

5-leetcode322. 零钱兑换

注意:×

  1. 注意要给初始值,这里给amount+1是因为后面需要1+dp[i-coin]防止溢出
  2. 因为零钱没有数量限制,所以在fori的循环内部需要对各种类型的零钱进行一个遍历
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount+1];
        Arrays.fill(dp,amount+1);
        // base case
        dp[0] = 0;
        // 转移
        for (int i = 1; i < dp.length; i++) {
            for (int coin : coins) {
                if (i - coin < 0){
                    continue;
                }
                dp[i] = Math.min(dp[i], 1+dp[i-coin]);
            }
        }
        return (dp[amount] == amount+1)? -1: dp[amount];
    }

6-leetcode139. 单词拆分

注意:×

  1. 问题很多,要非常小心的看。别的方法比较乱,没有这个简洁易懂
  2. 直接将List丢到HashSet中可以自动转换
  3. boolen数组
  4. 注意设置base case
  5. dp[i]=true的条件是dp[j] && hashSet.contains(s.substring(j,i))
  6. 注意为什么substring的取值是(j,i),因为substring是左闭右开的区间
    public boolean wordBreak(String s, List<String> wordDict) {
        int n = s.length();
        HashSet<String> hashSet = new HashSet<>(wordDict);
        boolean[] dp = new boolean[n + 1];
        // base case
        dp[0] = true;
        for (int i = 1; i < n + 1; i++) {
            for (int j = 0; j < i; j++) {
                if (dp[j] && hashSet.contains(s.substring(j,i))){
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[n];
    }

7-leetcode300. 最长递增子序列

注意:×

  1. 子序列是可以间断的
  2. dp[i]表示到这个位置nums[0...i]的最长递增子序列的值是多少
    public int lengthOfLIS(int[] nums) {
        int[] dp = new int[nums.length];
        int res = Integer.MIN_VALUE;
        Arrays.fill(dp, 1);

        for (int i = 0; i < dp.length; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[j] < nums[i]) {
                    dp[i] = Math.max(dp[i], 1 + dp[j]);
                }
            }
            res = Math.max(res, dp[i]);
        }
        return res;
    }

8-leetcode152. 乘积最大子序列

注意:×

  1. 这个题目和lc53的“最大子数组和”不一样,lc53是直接dp[i] = Math.max(nums[i], dp[i-1]*nums[i]);就好了,但是这里是乘积
  2. 乘积需要注意,当前状态下可能是一个负数乘积,到后面可能再乘一个负数就转正了
  3. 因此需要一个维护正数的值一个维护负数的值
  4. 编代码的时候注意, nmin = Math.min(nmax = Math.max(
    public int maxProduct(int[] nums) {
        int n = nums.length;
        if (n == 1) {
            return nums[0];
        }
        int nmin = 1;
        int nmax = 1;
        int res = Integer.MIN_VALUE;
        for (int i = 0; i < n; i++) {
            if (nums[i]<0){
                int tmp = nmin;
                nmin = nmax;
                nmax = tmp;
            }
            nmin = Math.min(nmin*nums[i], nums[i]);
            nmax = Math.max(nmax*nums[i], nums[i]);

            res = Math.max(res, nmax);
        }
        return res;
    }

作者:画手大鹏
链接:https://leetcode.cn/problems/maximum-product-subarray/solutions/7561/hua-jie-suan-fa-152-cheng-ji-zui-da-zi-xu-lie-by-g/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

9-leetcode416. 分割等和子集

注意:×

  1. 二维的dp
  2. 注意对sum是奇数的时候直接进行判断
  3. 在状态转移的时候,如果j-nums[i-1]表示当前的容量减去目前要加的货物量,如果不够,那就跟不选择这个货物的状态是一样的
  4. 如果还能装,那就从不装这个货物和装这个货物刚好装满二者之间选一个
  5. dp 数组的定义:dp[i][j] = x 表示,对于前 i 个物品,当前背包的容量为 j 时,若 x 为 true,则说明可以恰好将背包装满,若 x 为 false,则说明不能恰好将背包装满。
  6. 说实话还是有点蒙,慢慢揣摩
    public boolean canPartition(int[] nums) {
        int n = nums.length;
        int sum = 0;
        for (int num : nums) {
            sum = sum + num;
        }
        if (sum%2!=0){
            return false;
        }
        sum = sum / 2;

        // base case
        boolean[][] dp = new boolean[n + 1][sum + 1];
        for (int i = 0; i < n + 1; i++) {
            dp[i][0] = true;
        }
        for (int i = 0; i < sum + 1; i++) {
            dp[0][i] = false;
        }

        for (int i = 1; i < n + 1; i++) {
            for (int j = 1; j < sum + 1; j++) {
                if (j-nums[i-1]<0) {
                    // 容量不足
                    dp[i][j] = dp[i-1][j];
                }else {
                    dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i-1]];
                }
            }
        }

        return dp[n][sum];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值