【LeetCode】记录几个动态规划(DP)题目

本文通过分析LeetCode中的Coin Change(硬币找零)、Longest Increasing Subsequence(最长递增子序列)和Perfect Squares(完全平方数)三个动态规划题目,探讨了动态规划的解决思路和递推公式,包括如何初始化状态数组、遍历策略等关键点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[LeetCode] Coin Change 硬币找零
You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

Example 1:
Input: coins = [1, 2, 5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1

Example 2:
Input: coins = [2], amount = 3
Output: -1

Note:
You may assume that you have an infinite number of each kind of coin.

分析:
求硬币找零数目的最小值,在总钱数amount固定的条件下。极值问题,有条件(总钱数给定),有最优子问题(上一个小于amount的钱数的极值),考虑动态规划Dynamic Programming来做。
定义一个数组dp,dp[i]表示钱数为i时的最小硬币数目,递推公式为
dp[i] = min(dp[i], dp[i-coins[j]] + 1)
钱数为i时,比较 当前dp[i] (初始值) 和 上一个钱数下dp值( i-coins[j]为钱数i减去某一个硬币值,剩余的钱数,找到dp数组的记录)加1 做比较,取较小的更新dp[i]。
Q:dp初始化?因为总钱数给定,dp长度则有限,即amount;因为求最小值,初始化大于dp[i]可能的最大值即可;原本我用的INT_MAX,但这样会导致在dp[i-coins[j]]+1时越界,所以改为amount+1
Q:迭代写法中如何遍历?因为dp长度amount,要遍历一遍,求每个dp[i]时,所有coin值要遍历一遍,凡是小于i的硬币都有可能找到上一个子问题记录dp[i-coins[j]]

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        int tmp_max = amount+1; //硬币数目初始化最大值.因为coin值最小是1,数目最大可能值是amount
        vector<int> dp(amount+1, tmp_max); //因为有dp[0],dp的长度为amount+1
        dp[0] = 0; 
        for(int i=1; i<=amount; i++){
            for(int j=0; j<coins.size(); j++){
                if(coins[j]<=i)
                    dp[i] = min(dp[i], dp[i-coins[j]]+1);
            }
        }
        return dp[amount]==tmp_max?-1:dp[amount];
    }
};

[LeetCode] Longest Increasing Subsequence 最长递增子序列
Given an unsorted array of integers, find the length of longest increasing subsequence.

Example:
Input: [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.

Note:
There may be more than one LIS combination, it is only necessary for you to return the length.
Your algorithm should run in O(n2) complexity.

分析:
求子序列的最长。极值问题,有条件(子序列末位落于序列长度范围内),有最优子问题(上一个最长子序列,缩短序列长度的话),考虑动态规划方法。
定义数组dp,dp[i]表示以nums[i]结尾的序列中,LIS的长度,j是小于i的数(不一定是i-1,因为IS在原序列中是可以不连续的),递推公式为
dp[i] = max(dp[i], dp[j]+1)
对于每一个nums[i],从第一个数再搜索到i,如果发现有数小于nums[i],就更新dp[i],即比较 当前dp[i]值 和 那个小于num[i]的数的dp值加1 的大小。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> dp(nums.size(), 1);
        int res = 0;
        for(int i=0; i<nums.size(); i++){
            for(int j=0; j<i; j++){
                if(nums[i]>nums[j])
                    dp[i] = max(dp[i], dp[j]+1);
            }
            res = max(res, dp[i]); //并不知道LIS结尾落在序列哪位,所以需要有个变量res和每个dp[i]比较
        }
        return res;
    }
};

[LeetCode] Perfect Squares 完全平方数
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, …) which sum to n.

Example 1:
Input: n = 12
Output: 3
Explanation: 12 = 4 + 4 + 4.

Example 2:
Input: n = 13
Output: 2
Explanation: 13 = 4 + 9.

分析:
最简单的用四平方和定理,无知的我以前真没听过,详情见博文:[LeetCode] Perfect Squares 完全平方数 的解法一
我主要笔记一下动态规划方法。求最小数量平方数的和,极值问题,子问题是上一个数(i-j*j)也是最小平方数。
定义长度n+1的数组dp,dp[i]表示正整数i能少能由多个完全平方数组成。初始化dp[0]=0,其余值都为INT_MAX,i从0循环到n,j从1循环到i+j*j<=n,然后比较min(dp[i+j*j], dp[i]+1) 更新dp[i+j*j],递推公式
dp[i+j*j] = min(dp[i+j*j], dp[i]+1)
小trick:j也可以从1循环到i-j*j>0,比较min(dp[i], dp[i-j*j]+1)动态更新dp[i],只是,dp[i-j*j]不确定存在,可能是INT_MAX,+1会越界;而用dp[i]更新dp[i+j*j]的话,因为dp[i]一定存在,则没有越界问题。(前者在i较大时内层循环更新的dp较多,后者在i较小时更新的dp较多)
两个逻辑都是自底向上,都没毛病,但要理清细微之处的差别。

class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n+1, INT_MAX);
        dp[0] = 0;
        for(int i=0; i<=n; i++){
            for(int j=1; i+j*j<=n; j++){
                dp[i+j*j] = min(dp[i+j*j], dp[i]+1); //注意每次更新dp[i+j*j],不是dp[i]
            }
        }
        return dp[n];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值