赞
踩
前言:动态规划专题最后一篇,已完结,附动态规划经典公式
往期文章 :
动态规划常见问题:
*************************************************************************************************************************************************
1、组合问题:
377. 组合总和 Ⅳ
494. 目标和
518. 零钱兑换 II
2、True、False问题:
139. 单词拆分
416. 分割等和子集
组合问题公式
dp[i] += dp[i-num]
True、False问题公式
dp[i] = dp[i] || dp[i-num]
最大最小问题公式
dp[i] = min(dp[i], dp[i-num]+1) 或 dp[i] = max(dp[i], dp[i-num]+1)
当然拿到问题后,需要做到以下几个步骤:
背包问题技巧:
for(int i = 0; i < nums.size(); i++) {
for(int j = target; j >= 0; j--) {
}
}
for(int i = 0; i < nums.size(); i++) {
for(int j = nums[i]; j <= target; j++) {
}
}
for(int i = 1; i <= target; i++) {
for(int j = 0; j < nums.size(); j++) {
}
}
*************************************************************************************************************************************************
题目:
给定不同面额的硬币 coins
和一个总金额 amount
。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1
。
你可以认为每种硬币的数量是无限的。
示例:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
提示:
1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104
思路:
完全背包:取任意n种东西无限个,使结果为最大(最小)
我们采用自下而上的方式进行思考。定义f(i)
为组成金额 i 所需最少的硬币数量,假设在计算 f(i) 之前,我们已经计算出f(0)−f(i−1)
的答案。 则f(i)
对应的转移方程应为
f[j] = min(f[j], f[j - coins[i]] + 1);
其中coins[i]] 代表的是第 i 枚硬币的面值,即我们枚举最后一枚硬币面额是 coins[i]] ,那么需要从j - coins[i]
这个金额的状态f[j - coins[i]]
转移过来,再算上枚举的这枚硬币数量 1 的贡献,由于要硬币数量最少,所以 f(i) 为前面能转移过来的状态的最小值加上枚举的硬币数量 1。
假设coins = [1, 2, 3], amount = 6
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
int n = coins.size();
vector<int> dp(amount+1, amount+1);
dp[0] = 0;
for(int i = 0; i < n; i++) {
for(int j = coins[i]; j <= amount; j++) {
dp[j] = min(dp[j], dp[j - coins[i]] + 1);
}
}
return dp[amount] == amount+1? -1 : dp[amount];
}
};
题目:
给定一个由 不同 正整数组成的数组 nums
,和一个目标整数 target
。请从 nums
中找出并返回总和为 target
的元素组合的个数。数组中的数字可以在一次排列中出现任意次,但是顺序不同的序列被视作不同的组合。
题目数据保证答案符合 32 位整数范围。
示例:
输入:nums = [1,2,3], target = 4
输出:7
解释:
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。
提示:
1 <= nums.length <= 200
1 <= nums[i] <= 1000
nums 中的所有元素 互不相同
1 <= target <= 1000
进阶:如果给定的数组中含有负数会发生什么?问题会产生何种变化?如果允许负数出现,需要向题目中添加哪些限制条件?
思路:
爬楼梯问题
楼梯的阶数一共为target,一次可以走的步数为nums[i]。 一共有多少种走法?
转化成这种思想直接搞定,要注意的点就是C++数据会溢出
两种解决方案:
- dp改成unsigned类型
- dp时增加判断条件:两数之和不超过32位整数
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
vector<int> dp(target + 1, 0);
// 当不选取任何元素时,元素之和才为 0,因此只有 1 种方案
dp[0] = 1;
for(int i = 1; i <= target; i++) {
for(int j = 0; j < nums.size(); j++) {
// 如果不加第二个判断条件就会溢出,
// 解决方法:1. dp改成unsigned类型
// 2. 加上下面第二个判断条件,两数之和不超过32位整数,题目已经保证不超过32位整数范围
if(i >= nums[j] && dp[i] < INT_MAX - dp[i - nums[j]])
dp[i] = dp[i] + dp[i - nums[j]];
}
}
return dp[target];
}
};
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。