LeetCode 动态规划

一、动态规划问题的特点:
1) 问题具有最优子结构性质。如果问题的最优解所包含的 子问题的解也是最优的,我们就称该问题具有最优子结 构性质。

2)无后效性。当前的若干个状态值一旦确定,则此后过程的演变就只和这若干个状态的值有关,和之前是采取哪种手段或经过哪条路径演变到当前的这若干个状态,没有关系。

二、求解思路:
1. 将原问题分解为子问题

2.确定状态

和子问题相关的各个变量的一组取值,称之为一个“状 态”。

3.确定一些初始状态(边界状态)的值

  1. 确定状态转移方程

    不同的状态之间如何迁移,即如何从一个或多个“值”已知的 “状态”,求出另一个“状态”的“值”(递推型)。状态的迁移可以用递推公式表示,此递推公式也可被称作“状态转移方程”。

三、将递归思路转化为动态规划(递归需要大量堆栈空间,容易溢出)
重点:递归的n个参数 → 动态规划的n维数组
数组的下表:参数的取值范围
数组的元素值:递归函数的返回值
从边界值开始填充数组(递归的逆过程)

1、题目:Triangle
链接:http://oj.leetcode.com/problems/triangle/ (最短路径)
思路:动态规划,一维数组保存从当前位置开始的最小值,初始边界值为最后一行的值,转移方程如下

    public int minimumTotal(List<List<Integer>> triangle) {
        if(triangle.isEmpty())
            return 0;
        int n1 = triangle.size();
        if(n1 == 1)
            return triangle.get(0).get(0);
        List<Integer> list = triangle.get(n1 - 1);
        Integer[] d = new Integer[list.size()];
        list.toArray(d);
        for(int i = n1 - 2; i >= 0; i--){
            if(i == 0)
                return triangle.get(0).get(0) + (d[0] < d[1] ? d[0] : d[1]);
            List<Integer> temp = triangle.get(i);
            for(int j = 0; j <= temp.size() - 1; j++){
                d[j] = (d[j] < d[j + 1] ? d[j] : d[j + 1]) + temp.get(j);
            }
        }
        return d[0];
    }

2、题目:Word Break
链接:http://oj.leetcode.com/problems/word-break/
思路:动态规划。res[i]表示到字符串s中从0 - i的子串,是否可以用字典中的词来表示。
起始条件:res[0] =true;
转移方程:res[j] && wordDict.contains(s.substring(j, i + 1)) == true

    public boolean wordBreak(String s, List<String> wordDict) {
        if(s == null || s.length() == 0)
            return true;
        //res[i]表示到字符串i为止的子串,是否可以用字典中的词来表示
        boolean[] res = new boolean[s.length() + 1];
        res[0] = true;
        for(int i = 0; i < s.length(); i++){
            for(int j = 0; j <= i; j++){
                if(res[j] && wordDict.contains(s.substring(j, i + 1))){
                    res[i + 1] = true;
                    break;
                }
            }
        }
        return res[s.length()];
    }

3、题目:Unique Binary Search Trees
链接:http://oj.leetcode.com/problems/unique-binary-search-trees/
思路:可行的二叉搜索树是Catalan Number卡塔兰数。
这里写图片描述

    public int numTrees(int n) {
        if(n==0||n==1)
            return 1;
        int[] nums=new int[n+1];
        nums[0]=1;
        nums[1]=1;
        for(int i=2;i<=n;i++){
            for(int j=0;j<=i-1;j++){
                nums[i]+=nums[j]*nums[i-j-1];
            }
        }
        return nums[n];
    }

4、题目:Unique Paths II
链接:http://oj.leetcode.com/problems/unique-paths-ii/
思路:DP,到达(i,j)位置的路径数量 = 到达(i- 1,j)的路径数量+到达(i, j - 1)的路径数量
dp[i]:表示到达i列的路径数量。

    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length, n = obstacleGrid[0].length;
        //到达(i,j)位置的路径数量 = 到达(i- 1,j)的路径数量+到达(i, j - 1)的路径数量
        //dp[i]:表示到达i列的路径数量。
        int[] dp = new int[n];
        dp[0] = 1;
        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++){
                if(obstacleGrid[i][j] == 1)
                    dp[j] = 0;
                else if(j > 0)
                    dp[j] += dp[j - 1];
            }
        return dp[n - 1];
    }

5、题目:Climbing Stairs
链接:https://leetcode.com/problems/climbing-stairs/
思路:斐波那契数列,DP求解

    public int climbStairs(int n) {
        int[] dp = new int[n + 1];
        dp[0] = 1;
        dp[1] = 1;
        for(int i = 2; i <= n; i++){
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }

6、题目:Decode Ways
链接:http://oj.leetcode.com/problems/decode-ways/
思路:组合个数符合斐波那契数列。DP求解(待完善)

    public int numDecodings(String s) {
        int n = s.length();
        if (n == 0) return 0;
        int[] memo = new int[n+1];
        memo[n]  = 1;
        memo[n-1] = s.charAt(n-1) != '0' ? 1 : 0;
        for (int i = n - 2; i >= 0; i--)
            if (s.charAt(i) == '0') continue;
            else memo[i] = (Integer.parseInt(s.substring(i,i+2))<=26) ? memo[i+1]+memo[i+2] : memo[i+1];
        return memo[0];
    }

7、题目:Minimum Path Sum
链接:http://oj.leetcode.com/problems/minimum-path-sum/
思路:DP求解,从第一行开始,注意初始值的赋值

    public int minPathSum1(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        int[] dp = new int[n];
        dp[0] = grid[0][0];
        for(int i = 1; i < n; i++){
            dp[i] = dp[i - 1] + grid[0][i];
        }
        for(int i = 1; i < m; i++)
            for(int j = 0; j < n; j++){
                if(j >= 1)
                    dp[j] = grid[i][j] + (dp[j - 1] < dp[j] ? dp[j - 1] : dp[j]);
                else dp[j] += grid[i][j];
            }
        return dp[n - 1];
    }

8、题目:Best Time to Buy and Sell Stock
链接:https://leetcode.com/problems/best-time-to-buy-and-sell-stock/
思路:DP:min表示当前历史的最小值,max_profit表示当前状态的最大利润

    public int maxProfit(int[] prices) {
        //min表示当前历史的最小值
        //max_profit表示当前状态的最大利润
        int max_profit = 0, min = Integer.MAX_VALUE;
        for (int price : prices) {
            min = Math.min(min, price);
            max_profit = Math.max(max_profit, price - min);
        }
        return max_profit;
    }

9、题目:Maximum Subarray
链接:https://leetcode.com/problems/maximum-subarray/
思路:已知0 →k的max 为cursum,则0 → k + 1 的最大值为curSum > 0 ? curSum + num : num

    public int maxSubArray(int[] nums) {
        int max = Integer.MIN_VALUE, curSum = Integer.MIN_VALUE;
        for (int num : nums) {
            curSum = curSum > 0 ? curSum + num : num;
            max = max > curSum ? max : curSum;
        }
        return max;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值