动态规划Day07

70. 爬楼梯(进阶版)

卡码网:57. 爬楼梯(opens new window)

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬至多m (1 <= m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

输入描述:输入共一行,包含两个正整数,分别表示n, m

输出描述:输出一个整数,表示爬到楼顶的方法数。

输入示例:3 2

输出示例:3

提示:

当 m = 2,n = 3 时,n = 3 这表示一共有三个台阶,m = 2 代表你每次可以爬一个台阶或者两个台阶。

此时你有三种方法可以爬到楼顶。

  • 1 阶 + 1 阶 + 1 阶段
  • 1 阶 + 2 阶
  • 2 阶 + 1 阶
看到题目的第一想法

        使用完全背包

        完全背包:同一个物品可以无限次使用

        背包 n   物品 0~m

        使用0~m 达到背包容量j 有多少种方法’

        这道题求的是顺序数,需要考虑背包和物品的顺序,应该是先背包后物品

看到代码随想录之后的想法

        动规五部曲,很快的写出解题思路

        1确定dp数组以及对应下标的含义

        使用0~m ,dp[j] 到达j层阶梯有多少种方法

        2确定递推公式

        dp[j]=dp[j-m]+dp[j]

        3dp数组初始化

        dp[0]=1 一切的起源

        4确定遍历顺序

        从前往后

        先背包后物品 因为需要考虑顺序

        5手动推导dp数组

        6打印dp数组

        

自己实现过程中遇到的困难

        求排列,我之前写成求组合了

        排列是要考虑顺序的,组合不需要考虑顺序

import java.util.*;
import java.lang.*;
 
public class Main{
    public static void main (String[] args) {
        /*
        本题看起来是一道简单题目,稍稍进阶一下其实就是一个完全背包!
 
如果我来面试的话,我就会先给候选人出一个 本题原题,看其表现,如果顺利写出来,进而在要求每次可以爬[1 - m]个台阶应该怎么写。
 
顺便再考察一下两个for循环的嵌套顺序,为什么target放外面,nums放里面。
 
这就能考察对背包问题本质的掌握程度,候选人是不是刷题背公式,一眼就看出来了。
 
这么一连套下来,如果候选人都能答出来,相信任何一位面试官都是非常满意的。
 
本题代码不长,题目也很普通,但稍稍一进阶就可以考察完全背包,而且题目进阶的内容在leetcode上并没有原题,一定程度上就可以排除掉刷题党了,简直是面试题目的绝佳选择!
        */
         //Dp数组的定义和每个下标的含义
         //完全背包,可以选择自己
         // weight[m]=[1,2,3...m]
         // j 代表到达j有多少种方法
         //dp[j] 爬到i层有多少中方法
         //确定递推公式
         // dp[j] += dp[j-nums[i]];
         //dp数组初始化
        // dp[0]=1
         //确定遍历顺序
         //从前往后,从nums[i]开始
         //举例推导dp数组
         Scanner sc = new Scanner(System.in);
         //n 为楼顶
         int n = sc.nextInt();
         // m 为一步最远的
         int m = sc.nextInt();
         int[] weight = new int[m+1];
         for(int i=1;i<=m;i++){
            weight[i] = i;
         }
         int dp[] = new int[n+1];
         dp[0]=1;
         // 这道题是求排列,所以我下面的顺序是错的,我这个顺序是求组合
         /*for(int i=1;i<=m;i++){
             for(int j=weight[i];j<=n;j++){
                 dp[j]+=dp[j-weight[i]];
             }
         }*/
         for(int j=1;j<=n;j++){
            for(int i=1;i<=m;i++){
                 if(j>=i) dp[j]+=dp[j-i];
             }
         }
         System.out.println(dp[n]);
    }
}

322. 零钱兑换(求最小值)

力扣题目链接(opens new window)

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

你可以认为每种硬币的数量是无限的。

示例 1:

  • 输入:coins = [1, 2, 5], amount = 11
  • 输出:3
  • 解释:11 = 5 + 5 + 1

示例 2:

  • 输入:coins = [2], amount = 3
  • 输出:-1

示例 3:

  • 输入:coins = [1], amount = 0
  • 输出:0

示例 4:

  • 输入:coins = [1], amount = 1
  • 输出:1

示例 5:

  • 输入:coins = [1], amount = 2
  • 输出:2

提示:

  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 2^31 - 1
  • 0 <= amount <= 10^4
看到题目的第一想法

        可以凑成总金额所需的最少的硬币个数

        最小值应该怎么处理呢?没想到思路

看到代码随想录之后的想法

        动规五部曲,很快的写出解题思路

        1确定dp数组以及对应下标的含义

        使用0~m ,dp[j] 可以凑成总金额所需的最少的硬币个数

        2确定递推公式

        是否选中当前, 若选中则dp[j-coins[i]]+1

        dp[j]=Math.min(dp[j],dp[j-coins[i]]+1)

        3dp数组初始化

        dp[0]=0,其他都为Integer.MAX_VALUE

        4确定遍历顺序

        从前往后

        不需要考虑组合或排列,只是求最小值而已

        5手动推导dp数组

        6打印dp数组

        

自己实现过程中遇到的困难

        从dp[j]的定义来入手考虑递推公式,比如这道题dp[j]代表可以凑成总金额所需的最少的硬币个数,则递推公式为  ,要么选中当前物品 则为dp[j-coins[i]]+1 ,要么不选,则为dp[j](之前的值)

        求最小值,应该把其他元素都设为Integer.MAX_VALUE ,第一个元素为0

class Solution {
    public int coinChange(int[] coins, int amount) {
        //每次取最小值?
        //dp[i] 代表凑成总金额所需的硬币数目
        // 比如nums[0]=1 amount = 100 凑成1 需要dp[0]+1 or dp[1] 中的最小值 
        // 则凑成100 需要 dp[99]+1 or dp[100] 的最小值 ,dp[100-1] = dp[99]
        // dp数组的定义和下标的含义
        // 可以凑成j金额的最少硬币个数
        // 确定递推公式
        // 0~i凑成j金额的个数 每凑成一个+1
        // dp[j] = Math.min(dp[j-coins[i]]+1,dp[j])
        // dp数组初始化
        // dp[0] = 0,由于是凑成最小个数,则除开第一个外,其他的都是max
        // 确定遍历顺序
        // 举例推导dp数组
        
        int[] dp = new int[amount+1];
        dp[0]=0;
        for(int i=1;i<=amount;i++){
            dp[i] = Integer.MAX_VALUE;
        }
        for(int i=0;i<coins.length;i++){
            for(int j=coins[i];j<=amount;j++){
                //记录次数,每次加1
                // 只有j-coins[i] 不为最大值时,才有交换的必要,不然会把Integer.MAX_VALUE+1,造成溢出
                if(dp[j-coins[i]]!=Integer.MAX_VALUE)
                dp[j] = Math.min(dp[j],dp[j-coins[i]]+1);
            }
        }
        
        return dp[amount]==Integer.MAX_VALUE?-1:dp[amount];
    }
}

完全平方数

力扣题目链接(opens new window)

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

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

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

示例 1:

  • 输入:n = 12
  • 输出:3
  • 解释:12 = 4 + 4 + 4

示例 2:

  • 输入:n = 13
  • 输出:2
  • 解释:13 = 4 + 9

提示:

        看到题目的第一想法

        和上一题感觉差不多,也是求最小数量

        但物品为  各个数的平方,1 4 9 16.。。 要怎么来遍历?

        其实是用for循环来控制

        看到代码随想录之后的想法

        动规五部曲,很快的写出解题思路

        1确定dp数组以及对应下标的含义

        dp[j] 和为j的完全平方数的最小数量

        2确定递推公式

        是否选中当前, 若选中则dp[j-coins[i]]+1

        dp[j]=Math.min(dp[j],dp[j-i]+1)

        3dp数组初始化

        dp[0]=0,其他都为Integer.MAX_VALUE

        4确定遍历顺序

        从前往后

        不需要考虑组合或排列,只是求最小值而已

        5手动推导dp数组

        6打印dp数组

        

自己实现过程中遇到的困难

       按照for循环来确立物品的范围

        先背包后物品

        for(int i=1;i<=n;i++){

                //物品的平方要<=i

               for(int j=1;j*j<=i;j++){

                }

        }

        先物品后背包

        for(int i=1;i*i<=n;i++){

                //物品的平方要<=i

               for(int j=1;j<=m;j++){

                }

        }

        求最小值,应该把其他元素都设为Integer.MAX_VALUE ,第一个元素为0

  • class Solution {
        public int numSquares(int n) {
            //完全平方数和为背包容量j
            // 在完全平方数中选取能满足完全平方数和为背包容量j的最小个数
            // dp数组的定义和下标的含义
            // dp[j]为能满足容量j所需的完全平方数的最小个数
            // 确定递推公式
            // dp[j]=Math.min(dp[j],dp[j-某个平方]+1);
            // dp数组初始化
            // dp[0]=0,其他都为最大值
            // 确定遍历顺序
            // 可以重复使用,则从前往后
            // 举例推导dp数组
            // 平方为1 dp[0]=0,dp[1]=1;dp[2]=2;dp[3]=3;dp[4]=4;
            // 平方为4 dp[0]=0,dp[1]=1;dp[2]=2;dp[3]=3;dp[4]=1;
            
            int[] dp  = new int[n+1];
            for(int i=0;i<=n;i++){
                dp[i]=Integer.MAX_VALUE;
            }
            dp[0] = 0;
            for(int i=1;i*i<=n;i++){
                for(int j=i*i;j<=n;j++){
                    dp[j] = Math.min(dp[j],dp[j-i*i]+1);
                }
            }
            return dp[n];
        }
    }

  • 49
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值