day45 ● 70. 爬楼梯 (进阶) ● 322. 零钱兑换 ● 279.完全平方数

126 阅读2分钟

本文将介绍使用动态规划解决三个问题:爬楼梯、零钱兑换和完全平方数。动态规划是一种常用的算法思想,它可以在很多场景中得到应用。本文将以Java语言为例,给出这三个问题的动态规划实现,并进行分析和解释。

  1. 爬楼梯

问题描述:假设你正在爬楼梯。需要n步才能到达楼顶。每次可以爬1或2个台阶。你有多少种不同的方法可以爬到楼顶呢?

解题思路:本题可以使用动态规划的思想来解决。我们可以把问题转化为:在第i个台阶,有多少种方法可以到达它。由于每次可以爬1或2个台阶,因此到达第i个台阶可以从第i-1个和第i-2个台阶到达。因此,我们可以得到状态转移方程:dp[i] = dp[i-1] + dp[i-2]。其中,dp[i]表示到达第i个台阶的方法数,dp[i-1]表示从第i-1个台阶到达第i个台阶的方法数,dp[i-2]表示从第i-2个台阶到达第i个台阶的方法数。初始状态为:dp[0] = 1,dp[1] = 1。

Java代码实现:

public int climbStairs(int n) {
    if (n == 1 || n == 2) {
        return 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];
}
  1. 零钱兑换

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

解题思路:本题可以使用动态规划的思想来解决。我们可以把问题转化为:对于金额i,需要最少的硬币数为dp[i]。假设现在有一枚面额为coin的硬币,那么可以得到状态转移方程:dp[i] = min(dp[i], dp[i-coin]+1)。其中,dp[i]表示凑成金额i所需的最少硬币数,dp[i-coin]表示凑成金额i-coin所需的最少硬币数,+1表示需要加上这一枚硬币。初始状态为:dp[0] = 0。

Java代码实现:

public int coinChange(int[] coins, int amount) {
    int[] dp = new int[amount+1];
    Arrays.fill(dp, amount+1);
    dp[0] = 0;
    for (int coin : coins) {
        for (int i = coin; i <= amount; i++) {
            dp[i] = Math.min(dp[i], dp[i-coin]+1);
        }
    }
    return dp[amount] > amount ? -1 : dp[amount];
}
  1. 完全平方数

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

解题思路:本题可以使用动态规划的思想来解决。我们可以把问题转化为:对于正整数i,需要最少的完全平方数个数为dp[i]。假设现在有一个完全平方数jj,那么可以得到状态转移方程:dp[i] = min(dp[i], dp[i-jj]+1)。其中,dp[i]表示组成正整数i所需的最少完全平方数个数,dp[i-jj]表示组成正整数i-jj所需的最少完全平方数个数,+1表示需要加上这一个完全平方数。初始状态为:dp[0] = 0。

Java代码实现:

public int numSquares(int n) {
    int[] dp = new int[n+1];
    Arrays.fill(dp, Integer.MAX_VALUE);
    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];
}

总结

动态规划是一种常用的算法思想,可以在很多场景中得到应用。本文以三个问题为例,介绍了动态规划的思想和Java代码实现。在实际应用中,需要根据具体问题进行分析和设计,确定状态转移方程和初始状态,以达到最优的效果。