爬楼梯(进阶)
这道题其实可以抽象为完全背包问题,要爬的总台阶数就是背包容量,一次能爬的台阶数目就是要装入背包的物品,物品可以无穷次使用。同时要注意这道题要求的是排列方法数目,要注意背包和物品的遍历顺序。
#include <bits/stdc++.h>
using namespace std;
int main() {
int m, n;
while(cin >> n >> m) {
vector<int> dp(n + 1, 0);
dp[0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(j <= i)
dp[i] += dp[i - j];
}
}
cout << dp[n] << endl;
}
return 0;
}
零钱兑换
这道题求最小的硬币数目(可以将硬币金额看做weight,value看做1,求最小价值),硬币无限次使用,是一个纯完全背包问题,不涉及内外循环顺序的问题。
但在初始化和处理过程中有一些细节需要注意,一方面是要求最小值,所以初始化时非0下标应该初始化为int类型最大值。同时在计算dp数组过程中要考虑溢出问题。
class Solution{
public:
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount + 1, INT_MAX);
dp[0] = 0;
for(int i = 0; i < coins.size(); i++) {
for(int j = coins[i]; j <= amount; j++) {
if(dp[j - coins[i]] != INT_MAX) // 避免从最大值溢出
dp[j] = min(dp[j], dp[j - coins[i]] + 1);
}
}
if(dp[amount] == INT_MAX) return -1;
return dp[amount];
}
};
完全平方数
这道题和上面的零钱兑换一样,只是对于任意的整数都能写成完全平方数的和,所以其实可以不用判断溢出情况,每一个背包情况都会赋值。
class Solution{
public:
int numSquares(int n) {
vector<int> dp(n + 1, INT_MAX);
dp[0] = 0;
for(int i = 1; i * i <= n; i++) { // 先遍历物品
for(int j = i * i; j <= n; j++) { // 后遍历背包
dp[j] = min(dp[j], dp[j - i * i] + 1);
}
}
return dp[n];
}
};