zoukankan      html  css  js  c++  java
  • LeetCode|动态规划入门三题

    一、爬楼梯

    假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
    每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
    注意:给定 n 是一个正整数。

    示例 1:

    输入: 2
    输出: 2
    解释: 有两种方法可以爬到楼顶。

    1. 1 阶 + 1 阶
    2. 2 阶

    示例 2:

    输入: 3
    输出: 3
    解释: 有三种方法可以爬到楼顶。

    1. 1 阶 + 1 阶 + 1 阶
    2. 1 阶 + 2 阶
    3. 2 阶 + 1 阶

    解题思路

    本题是动态规划里面最简单的题目了,印象中第一次见到这个题目还是在蓝桥杯的练习题中。
    虽然本篇是动态规划的入门文章,但是我并不会说任何书面上写的动态规划算法的定义。这是因为动态规划的概念还是很复杂的,看完动态规划算法的思路和特性估计你就被动态规划吓到了。
    关于动态规划的定义,我只说一句个人看法——动态规划求的是全局最优解。全局意味着动态规划是要全盘考虑问题的。话不多说,我们先从题目解答中开始体会。

    本题中,我们看到,爬1阶梯只有1种方法,爬2阶梯有2种方法。那我们就能想到,爬3阶梯有3种,这是因为爬3阶等于爬2阶的基础上再爬1阶。同理爬4阶等于在3阶的基础上爬1阶和2阶的基础上爬2阶;

    这种“站在巨人的肩膀上”的思路就是动态规划的思想。

    实现代码

    首先我们能想到的是使用递归的方法。递归方法跟我们上面的思路正好反过来。递归的思路是爬n阶楼梯的方式等于爬n-1阶楼梯的次数加上n-2阶楼梯的次数。但是递归方法不会保存计算结果,导致有很多重复的计算,因此超时也就不可避免了。

    1. 递归
    class Solution {
        public int climbStairs(int n) {
            if(n==1) return 1;
            if(n==2) return 2;
            return climbStairs(n-1)+climbStairs(n-2);
        }
    }
    

    运行结果:超时

    2. 动态规划

    动态规划的整体思路跟递归很像,但是会通过一个dp数组保存计算结果。这样计算速度就会快很多。

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

    二、连续子数组的最大和

    给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

    示例 1:

    输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
    输出:6
    解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

    示例 2:

    输入:nums = [1]
    输出:1
    示例 3:

    输入:nums = [0]
    输出:0

    示例 4:

    输入:nums = [-1]
    输出:-1

    示例 5:

    输入:nums = [-100000]
    输出:-100000

    提示:

    1 <= nums.length <= 3 * 104
    -105 <= nums[i] <= 105

    进阶: 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。

    解题思路

    动态规划是本题的最优解。动态规划的思路也很清晰。假设有一个数组[-2,1],那么我们可以看到[-2,1]两数的和比数组第二个元素1要小。因此连续子数组的最大和为1。当数组变为[-2,1,-3]的时候,我们已经知道[-2,1]这部分数组的连续子数组的最大和为1。那么1跟[-3]比较是1比较大,因此这个数组的连续子数组的最大和依然为1。

    简而言之,每次数组新增一个元素,都需要跟增加前的数组的连续子数组的最大和相比较,结果较大的那个就是新增后数组的连续子数组的最大和。代码实现如下:

    实现代码

    class Solution {
        public int maxSubArray(int[] nums) {
            int dp[]=new int[nums.length];
            dp[0]=nums[0];
            int max=dp[0];
            for(int i=1;i<dp.length;i++){
                //compare the num last status plus current num and current num 
                dp[i]=Math.max(dp[i-1]+nums[i],nums[i]);
                //compare max and dp[i],it will update max
                max=Math.max(max,dp[i]);
            }
            return max;
        }
    }
    

    三、游戏币组合

    硬币。给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)

    示例1:

    输入: n = 5
    输出:2
    解释: 有两种方式可以凑成总金额:
    5=5
    5=1+1+1+1+1

    示例2:

    输入: n = 10
    输出:4
    解释: 有四种方式可以凑成总金额:
    10=10
    10=5+5
    10=5+1+1+1+1+1
    10=1+1+1+1+1+1+1+1+1+1

    说明:

    注意:

    你可以假设:

    0 <= n (总金额) <= 1000000

    解题思路

    这道题的状态转移方程似乎不那么容易看出来,不过我们可以列一个表格来观察一下,看能不能发现一点什么规律。

    硬币数总面值 1 2 3 4 5 6 7 8 9 10
    1 1 0 0 0 1 0 0 0 0 1
    2 0 1 0 0 0 1 0 0 0 1
    3 0 0 1 0 0 0 1 0 0 0
    4 0 0 0 1 0 0 0 1 0 0
    5 0 0 0 0 1 0 0 0 1 0
    6 0 0 0 0 0 1 0 0 0 1
    7 0 0 0 0 0 0 1 0 0 0
    8 0 0 0 0 0 0 0 1 0 0
    9 0 0 0 0 0 0 0 0 1 0
    10 0 0 0 0 0 0 0 0 0 1
    f(n) 1 1 1 1 2 2 2 2 2 4

    首先我们定义一个dp数组和所有的硬币数组

     int[] dp = new int[n + 1];
     int[] coins = {1,5,10,25};
    

    并且设置dp[0]=1,为边界的条件,作为完美能被一个硬币表示的情况为 1。
    dp[i]+=dp[i-coin]当i-coin为0时结果为1,表示一个硬币能表示的情况。

    我们可以简单的得出状态转移方程

    // 题目中需求对结果取模1000000007
    dp[i] = dp[i] + dp[i - coin] 
    

    代码实现

    class Solution {
        public int waysToChange(int n) {
           int dp[]=new int[n+1];
           int coins[]={1,5,10,25};
           dp[0]=1;
    
            for(int coin : coins) {
                for(int i = coin; i <= n; i++) {
                    dp[i] = (dp[i] + dp[i - coin]) % 1000000007;
                }
            }
            return dp[n];
        }
    }
    
  • 相关阅读:
    Elasticsearch集成IKAnalyzer分析器
    Elasticsearch操作Document文档
    ElasticSearch简介
    Lucene查询索引
    Lucene索引维护(添加、修改、删除)
    Lucene使用IKAnalyzer分词
    Lucene搜索引擎入门
    MySQL优化(四)——读写分离
    MySQL优化(三)——主从复制
    Java 身份证验证工具类代码模板
  • 原文地址:https://www.cnblogs.com/rever/p/15007125.html
Copyright © 2011-2022 走看看