刷leetcode hot100--动态规划3.9
第一题:打家劫舍【11:54】
不能同时取相邻情况下的max
dp[n] = max(不包含dp[n-1])+nums[n]
完整写出dp【n】含义、递推公式、初始化确实有利于思考,最后再用少量ex检验自己的思考对不对
但还是有bug:最后return的时候按惯例return了dp[nums.size()],但由于不能相邻的原因,dp[nums.size()-1]必然不包含dp[nums.size()-2],所以倒数第二个数也可能是最大的!!!!
另一种思路?dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
class Solution {
public:
int rob(vector<int>& nums) {
//dp[n]:最后一个偷n,偷窃到的最高金额
vector<int> dp(nums.size());
//dp[n]=maxdp[i]+nums[n],维护max变量
dp[0] = nums[0];
if(nums.size()==1){
return dp[0];
}
dp[1] = nums[1];
int max = dp[0];
for(int i = 2;i<nums.size();i++){
dp[i] = max+nums[i];
if(dp[i-1]>max){
max = dp[i-1];
}
}
return dp[nums.size()-1] > dp[nums.size()-2] ? dp[nums.size()-1] : dp[nums.size()-2];//倒数第二个也能是max
}
};
第二题:完全平方数【没思路】
第三题:零钱兑换
dp[n]:和为n的最小硬币数
dp[n] = min(dp[n-coin[i]]+1,dp[n])
初始化:1.dp[0]=0【amount = 0的时候要能返回0,而不是-1】
2.其他dp应该初始化INT_MAX
tips:INT_MAX+1会越界
初版代码问题:1、只初始化了dp[i]==coins[j]的情况没有考虑如果e.g.10拿6块凑不了怎么办;以及初始化dp[i]=coins[j]没有用,会在for遍历中覆盖
2.dp[i] = min+1时,INT_MAX也加一导致越界
3.他没说coins是按递增排列,有个用例是最后一个coin很小。所以:
if (i >= coins[j]) { ****} else {break; } 如果i<coins[j]就break的逻辑是错的
p.s.还是没懂:为啥amount=11,coins=[1,2,5]输出-1,打印dp[i]都是10000????cout还不给输出[可能和flush有关]
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
if(amount == 0){
return 0;
}
//dp[n]金额n所需最少硬币数
vector<int> dp(amount+1);
//dp[n] = mindp[n-coins[i]]+1
//初始化
dp[0] = 0;//amount==0
int size = 0;
for(int i = 0;i<coins.size();i++){
if(coins[i]<=amount){//<=
dp[coins[i]] = 1;
}else{
size = i+1;
}
}
int min = 9999;
//你可以认为每种硬币的数量是无限的。
for(int i=1;i<=amount;i++){//dp[i]
for(int j = 0;j<size;j++){//coins[j]
if(i>=coins[j]){
min = dp[i-coins[j]]<min ? dp[i-coins[j]] : min;
}else{
break;
}
}
dp[i] = min+1;
min = 9999;
}
if(dp[amount] ==10000 ){
return -1;
}else{
return dp[amount];
}
}
};
ac代码如下:
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
if (amount == 0) {
return 0;
}
// dp[n] 表示金额 n 所需的最少硬币数
vector<int> dp(amount + 1, INT_MAX); // 初始化为最大值
dp[0] = 0; // amount == 0 时需要 0 个硬币
int min = INT_MAX;
// 遍历所有金额
for (int i = 1; i <= amount; i++) {
// 遍历所有硬币
for (int j = 0; j < coins.size(); j++) {
if (i >= coins[j]) {
// 如果 dp[i - coins[j]] 不是初始值,则更新 minCoins
if (dp[i - coins[j]] != INT_MAX) {
min = std::min(min, dp[i - coins[j]]);
}
}
// else {
// break;
// }
}
// cout << "dp[" << i << "] = " << dp[i] << endl;
if (min != INT_MAX) {
dp[i] = min + 1;
}
min = INT_MAX;
}
// 如果 dp[amount] 仍然是初始值,说明无法凑出 amount
if (dp[amount] == INT_MAX) {
return -1;
} else {
return dp[amount];
}
}
};