代码随想录打卡第三十九天

代码随想录–动态规划部分

day 38 休息
day 39 动态规划第六天



一、力扣322–零钱兑换

代码随想录题目链接:代码随想录

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。

硬币数量无限,说明是完全背包

dp[j]的含义就是凑成j所需要的最少的硬币个数,背包容量上限为amount

当遍历到coins[i]时,凑足j - coins[i]面额的最少个数为dp[j - coins[i]],所以递推公式为
d p [ j ] = m i n ( d p [ j ] , d p [ j − c o i n s [ i ] ] + 1 ) dp[j] = min(dp[j], dp[j-coins[i]] + 1) dp[j]=min(dp[j],dp[jcoins[i]]+1)
+1是因为要用coins[i]这个硬币

这里看到是min了,所以初始化时应该用INT_MAX填充数组

但是第一位应该是0,即dp[0] = 0,因为凑足0元一定是用0个硬币

套公式写就好了

代码如下:

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 + 1; 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];
    }
};

这里if (dp[j - coins[i]] != INT_MAX)是为了判断是否初始化了,如果没有初始化就跳过这个

二、力扣279–完全平方数

代码随想录题目链接:代码随想录

给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。

背包容量为n,物品价值为 i 2 i^2 i2,且物品无限,是完全背包,且无所谓排序还是组合

和上题一样,只是在物品遍历处注意上限是i*i<n

代码如下:

class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1, INT_MAX);
        dp[0] = 0;
        for(int i = 0; i * i <= n; i ++ )
            for(int j = i * i; j < n + 1; j ++)
                if(dp[j - i*i] != INT_MAX) 
                    dp[j] = min(dp[j], dp[j - i * i] + 1);
        return dp[n];
    }
};

三、力扣139–单词拆分

代码随想录题目链接:代码随想录

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。

单词可以重复使用,是完全背包问题

并且不用考虑单词拆分的问题,字典中的词拼起来不能合成s就返回false即可

dp[i]代表长度为i时,若为true则可以组成,否则不行。其实难点在于遍历

遍历想法可以是:当确认dp[j] = true,且[j,i]这个区间的子串出现在字典中,那么dp[i]也应该是true

这样dp的每个元素可以代表出来字符串s在哪些位置上是可以由字典组成的,检查最后一位就可以了

代码如下:

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
        vector<bool> dp(s.size() + 1, false);
        dp[0] = true;
        for(int i = 1; i < s.size() + 1; i ++)
            for(int j = 0; j < i; j ++)
            {
                string subs  = s.substr(j, i - j);
                if(dp[j] && wordSet.find(subs) != wordSet.end())
                    dp[i] = true;
            }
        return dp[s.size()];
    }   
};

四、卡码网56–携带矿石资源

代码随想录题目链接:代码随想录

你是一名宇航员,即将前往一个遥远的行星。在这个行星上,有许多不同类型的矿石资源,每种矿石都有不同的重要性和价值。你需要选择哪些矿石带回地球,但你的宇航舱有一定的容量限制。

给定一个宇航舱,最大容量为 C。现在有 N 种不同类型的矿石,每种矿石有一个重量 w[i],一个价值 v[i],以及最多 k[i] 个可用。不同类型的矿石在地球上的市场价值不同。你需要计算如何在不超过宇航舱容量的情况下,最大化你所能获取的总价值。

物品不是只能使用一次,也不是无限使用,而是有次数了

所以这个题变成了多种背包问题,解决方法就是把多重背包展开成01背包问题

如第i个物品有n件,那就展开成i+n个物品,每个只有一件,且价格一样

代码如下:

#include<iostream>
#include<vector>
using namespace std;
int main() {
    int bagWeight,n;
    cin >> bagWeight >> n;
    vector<int> weight(n, 0);
    vector<int> value(n, 0);
    vector<int> nums(n, 0);
    for (int i = 0; i < n; i++) cin >> weight[i];
    for (int i = 0; i < n; i++) cin >> value[i];
    for (int i = 0; i < n; i++) cin >> nums[i];

    vector<int> dp(bagWeight + 1, 0);

    for(int i = 0; i < n; i++) { // 遍历物品
        for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
            // 以上为01背包,然后加一个遍历个数
            for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++) { // 遍历个数
                dp[j] = max(dp[j], dp[j - k * weight[i]] + k * value[i]);
            }
        }
    }

    cout << dp[bagWeight] << endl;
}

总结

在这里插入图片描述

  • 14
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值