一、题目打卡
1.1 爬楼梯
题目链接:57. 爬楼梯(第八期模拟笔试)
#include<iostream>
#include<vector>
using namespace std;
void solve(int &n, int &m){
vector<int> dp(n+1,0);
dp[0] = 1;
for(int i = 0 ; i <= n ; i++){
for(int j = 1 ; j <= m;j++){
if(i >= j) dp[i] = dp[i] + dp[i - j];
}
}
cout << dp[n];
}
int main(){
int m, n;
while(cin >> n >> m){
solve(n, m);
}
return 0;
}
这个递推的一个思想就是在最终目标点前面的m个步长以内,可以一步到达,所以方法累加就相当于是这个m个步长中所有的方法的总和,或者是思考为背包问题,因为每次走的步数都是从 1 开始到 m 的,因而就变成了一个完全背包问题。
1.2 零钱兑换
题目链接:. - 力扣(LeetCode)
// class Solution {
// public:
// static bool cmp(int &a, int &b){
// return a > b;
// }
// // vector<double> path;
// int size = 0;
// double sum = 0.0;
// int minSize = INT_MAX;
// // bool backtrack(vector<int>& coins, int& amount, int startInd){
// // if(sum == amount) return true;
// // if(sum > amount) return false;
// // for(int i = startInd; i < coins.size();i++){
// // path.push_back(coins[i]);
// // sum += coins[i];
// // if(backtrack(coins, amount, 0)) return true;
// // path.pop_back();
// // sum -= coins[i];
// // }
// // return false;
// // }
// void backtrack(vector<int>& coins, int& amount, int startInd){
// if(sum == amount){
// minSize = min(minSize, size);
// return;
// }
// if(sum > amount) return;
// for(int i = startInd; i < coins.size();i++){
// size++;
// sum += coins[i];
// backtrack(coins, amount, 0);
// size--;
// sum -= coins[i];
// }
// }
// int coinChange(vector<int>& coins, int amount) {
// sort(coins.begin(),coins.end(),cmp);
// backtrack(coins, amount, 0);
// if(minSize != INT_MAX) return minSize;
// // if(backtrack(coins, amount, 0)){
// // for(auto &it : path){
// // cout << it << " ";
// // }
// // return path.size();
// // }
// return -1;
// }
// };
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount + 1,INT_MAX); // 从 0 到 i 任意选择,组合成 amount 的最小硬币数
dp[0] = 0;
for(int i = 0; i <= amount ;i++){
for(int j = 0 ; j < coins.size(); j ++){
if(i >= coins[j] && dp[i - coins[j]] != INT_MAX) dp[i] = min(dp[i], dp[i - coins[j]] + 1);
}
}
// cout << dp[amount];
// if(dp[amount] == amount) return dp[amount];
// return -1;
if(dp[amount] == INT_MAX) return -1;
return dp[amount];
}
};
回溯不能用贪心的思想,比如419,408, 186, 83 的那个测试案例,如果回溯用排序以后的贪心方法来计算的话,就会导致83用的过多,具体原因我也说不上来,然后就是把这个问题考虑为一个完全背包的问题,因为每个硬币可以重复使用,不过我在一开始做这个题目的时候,有一点没有想明白,就是这个题目是需要凑满,计算最小的个数,那么凑满这个应该怎么表示呢?虽然最后写出来了,但是感觉对这一点我还理解的不是很透彻,我感觉是因为判断条件的保证,这样就相当于从小开始递推,只要是赋值的位置,就一定保证是最小的,到最后就一定可以凑的满。
另一个题目需要注意的点是,这个是求的是元素的个数,因而和组合的顺序是没有关系的,因而和遍历背包或者遍历物品的顺序是无关的。
1.3 完全平方数
题目链接:. - 力扣(LeetCode)
class Solution {
public:
int numSquares(int n) {
vector<int> dp(n + 1, INT_MAX);
dp[0] = 0;
for(int k = 1, i = k * k; i <= n ; k++, i = k*k){
// cout << i << endl;
for(int j = i ; j <= n;j++){
if(dp[j - i] != INT_MAX) dp[j] = min(dp[j], dp[j - i] + 1);
}
}
// for(auto& it: dp){
// cout << it << " ";
// }
return dp[n];
}
};
和上面那个题几乎是一模一样的,不过注意的是,因为这个是从1开始的,所以不存在最终凑不满的情况。