leetcode动态规划 dynamic programming ——300/509/62/121/70/279/221

零、动态规划

https://cloud.tencent.com/developer/article/1817113

1.什么样的问题可以考虑使用动态规划解决呢?

如果一个问题,可以把所有可能的答案穷举出来,并且穷举出来后,发现存在重叠子问题,就可以考虑使用动态规划。(比如跳台阶问题,自顶向下的话,会有多次重复调用,即重叠子问题,因此可以逆向自底向上思考,考虑动态规划)

动态规划比较经典的场景:

  • 最长递增子序列
  • 最小编辑距离
  • 背包问题
  • 凑零钱问题

2.动态规划的解题思路

动态规划的核心思想就是拆分子问题,记住过往,减少重复计算,并且动态规划一般都是自底向上的。
思路如下:

  • 穷举分析
  • 确定边界
  • 找出规律,确定最优子结构
    最优子结构
    一道动态规划问题,其实就是一个递推问题。假设当前决策结果是f(n),则最优子结构就是要让 f(n-k) 最优,最优子结构性质就是能让转移到n的状态是最优的,并且与后面的决策没有关系,即让后面的决策安心地使用前面的局部最优解的一种性质
  • 写出状态转移方程
    接下来以为leetcode300题为例,

一、300最长递增子序列

1.题目

https://leetcode-cn.com/problems/longest-increasing-subsequence/
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

2.思路

参考:https://cloud.tencent.com/developer/article/1817113
依次遍历先找到以i为尾节点的最长的递增序列,然后根据记录的之前最大值的记录和当前值比较

3.代码

class Solution {
public int lengthOfLIS(int[] nums) {
if (nums.length == 0) {
return 0;
        }
int[] dp = new int[nums.length];
//初始化就是边界情况
        dp[0] = 1;
int maxans = 1;
//自底向上遍历
for (int i = 1; i < nums.length; i++) {
            dp[i] = 1;
//从下标0到i遍历
for (int j = 0; j < i; j++) {
//找到前面比nums[i]小的数nums[j],即有dp[i]= dp[j]+1
if (nums[j] < nums[i]) {
//因为会有多个小于nums[i]的数,也就是会存在多种组合了嘛,我们就取最大放到dp[i]
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
//求出dp[i]后,dp最大那个就是nums的最长递增子序列啦
            maxans = Math.max(maxans, dp[i]);
        }
return maxans;
    }
}

二、509斐波那契数

https://leetcode-cn.com/problems/fibonacci-number/submissions/
解法可参考上一篇动态化搜索的博文

三、62不同路径

1.题目

https://leetcode-cn.com/problems/unique-paths/
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

2.思路

1.暴力解法,dfs但是需要加map记录已经走过的节点 key存储节点,value存储该节点对应的路径数
2.动态规划

3.代码

1.暴力解法

class Solution {
    public int uniquePaths(int m, int n) {
        //dfs尝试
        int count =0;
        Map<Pair,Integer> cache = new HashMap<>();
        return dfs(m,n,0,0,cache,count);


    }
    public int dfs(int m, int n, int r,int l,Map<Pair, Integer> cache,int count){
        //cache缓存已经走过的节点
        Pair p = new Pair(r,l);
        if (r == m-1 || l == n-1) return 1;
        if (cache.containsKey(p)) return cache.get(p);
        cache.put(p,dfs(m,n,r+1,l,cache,count)+dfs(m,n,r,l+1,cache,count));
        return cache.get(p);

    }
}

2.动态规划

class Solution {
    public int uniquePaths(int m, int n) {
        //存储每个ij对应可走的路径数
        int [][] res = new int [m][n];
        //边界位置可走的方案只有一个 
        for (int i=0; i<m;++i){
            res[i][0]=1;
        }
        for (int j=0; j<n;++j){
            res[0][j]=1;
        }
        for(int i=1;i<m;++i){
            for(int j=1;j<n;++j){
                res[i][j]=res[i-1][j]+res[i][j-1];
            }
        }
        return res[m-1][n-1];

    }
}

三、121买卖股票的最佳时机

1.题目

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

2.思路

动态规划
记录当前手里持股和不持股的现金流,依次遍历即可找到最大的现金流

3.代码

暴力解法——枚举每一天对应的利润,取最大值,会超时

class Solution {
    public int maxProfit(int[] prices) {
        //base case

        if (prices.length < 2) return 0;
        int maxPrice=0;
        for(int i=1; i<prices.length; ++i){
            for(int j=0; j<i;++j){
                maxPrice = Math.max(prices[i]-prices[j],maxPrice);
            }
            
        }
        return maxPrice;
    }
}

动态规划

class Solution {
    public int maxProfit(int[] prices) {
        //base case
        if (prices.length < 2) return 0;
        //存储持股或不持股状态下的当前现金流
        int[][] res = new int[prices.length][2];
        //初始化第一天现金流为0;第一天持股现金流为第一天股票的交易额;
        res[0][0]=0;
        res[0][1] = -prices[0];
        //从第二天遍历找到
        for(int i=1; i<prices.length;i++){
            //不持股的两种情况1.i-1不持股,什么都没做;2.i-1持股,i抛售
            res[i][0] = Math.max(res[i-1][0], prices[i]+res[i-1][1]);
            //持股的两种情况1.i-1持股,什么都没做;2.i-1不持股,i买入
            res[i][1] =Math.max(res[i-1][1], -prices[i]);
        }
        //最后手里一定没有股票
        return res[prices.length-1][0];
    }
}

四、70爬楼梯

1.题目

https://leetcode-cn.com/problems/climbing-stairs/
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

2.思路

暴力解法会超时,累计和的问题可采用滚动数组的方法,记录i-2 i-1和sum的值,不断更新

3.代码

class Solution {
    public int climbStairs(int n) {
        //base case
        if (n<=2) return n;
       //滚动数组思想,即i的方法数为i-1和i-2的方法数的总和
       int n1 = 1;
       int n2 =0;
       int sum = 2;
        for(int i=3; i<=n;++i){
            n2 = n1;
            n1 =sum;
            sum = n1+n2; 
        }
        return sum;
    }
}

五、279 完全平方数

1.题目

https://leetcode-cn.com/problems/perfect-squares/
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。

2.思路

https://leetcode-cn.com/problems/perfect-squares/solution/wan-quan-ping-fang-shu-by-leetcode-solut-t99c/

3.代码

class Solution {
    public int numSquares(int n) {
        //枚举每个数和剩余数的可能性总和
        int[] res = new int[n+1];
        res[0]=0;
        for(int i =1; i<=n;i++){
            int minn =Integer.MAX_VALUE;
            for(int j=1; j*j<=i;++j){
                minn = Math.min(res[i-j*j],minn);
            }
            res[i]=minn+1;
        }
        return res[n];
    }
}

六、221最大正方形

1.题目

https://leetcode-cn.com/problems/maximal-square/
在一个由 ‘0’ 和 ‘1’ 组成的二维矩阵内,找到只包含 ‘1’ 的最大正方形,并返回其面积。

2.思路

1.暴力解法
找到最长的边,然后计算最大的面积
2.动态规划

3.代码

1.暴力解法

class Solution {
    public int maximalSquare(char[][] matrix) {
        //暴力解法
        //正方形问题的关键需要判断最大的边长
        int row = matrix.length;
        int line = matrix[0].length;
        int maxSide =0;
        if(row==0||line==0 || matrix == null) return maxSide;
        //遇到的1视为左上角
        for(int i = 0; i<row;++i){
            for(int j=0; j<line;++j){
                if (matrix[i][j] == '1'){
                    maxSide = Math.max(1, maxSide);
                    int tempSide = Math.min(row-i, line-j) ;
                    //以i为左上角,找到当前最大的边长
                    for(int k=1;k<tempSide;++k){
                        boolean judge = true;
                        if (matrix[i+k][j+k]=='0') break;
                        for(int m=0; m<k;++m){
                            //break的意思是跳出当前循环,如果不加指示符,还会执行下面的语句
                            if (matrix[i+m][j+k] =='0' || matrix[i+k][j+m]=='0' ){
                                judge =false;
                                break;
                            } 
                        }
                        if(judge){
                            maxSide = Math.max(maxSide,k+1);
                        }else{
                            break;
                        }
                        
                    }
                }
            }
        }
        int maxSquare = maxSide*maxSide;
        return maxSquare;
    }
    
}

2.动态规划

class Solution {
    public int maximalSquare(char[][] matrix) {
        //暴力解法
        //正方形问题的关键需要判断最大的边长
        int row = matrix.length;
        int line = matrix[0].length;
        int maxSide =0;
        if(row==0||line==0 || matrix == null) return maxSide;
        //遇到的1视为右下角,取左边,左上,上的最小值+当前值1
        int [][] temp = new int[row][line];
        for (int i =0; i < row; ++i){
            for(int j=0; j<line;++j){
                if(matrix[i][j] == '1'){
                    if(i==0||j==0) {
                        temp[i][j]=1;
                    }else{
                        temp[i][j] = Math.min(Math.min(temp[i-1][j],temp[i-1][j-1]),temp[i][j-1])+1;
                    }
                }
                maxSide = Math.max(maxSide,temp[i][j]);
            }
            
        }
        
        int maxSquare = maxSide*maxSide;
        return maxSquare;
    }
    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值