c++之背包问题循环使用技巧

一、如果是0-1背包,先物再包,逆序
Leetcode416. 分割等和子集
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum=0;
        for(auto x:nums) sum+=x;
        if(sum%2!=0) return false;
//dp[i]表示是否能组合成总数为i
        vector<bool> dp(sum/2+1,false);
        dp[0]=true;
        for(auto y:nums)
        {
            for(int i=dp.size()-1;i>=0;i--)
            {
                if(i-y>=0)
                 dp[i]=dp[i] || dp[i-y];
            }
        }
        return dp[sum/2];
    }
};

二、如果是完全背包,先物再包,顺序
Leetcode322零钱兑换
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
可以写出状态转移方程:
1、先确定「状态」,也就是原问题和子问题中变化的变量。由于零钱数量无限,所以唯一的状态就是目标金额 amount。
2、然后确定 dp 函数的定义:当前的目标金额是 n,至少需要 dp(n) 个零钱凑出该金额。
3、然后确定「选择」并择优,也就是对于每个状态,可以做出什么选择改变当前状态。具体到这个问题,无论当前的目标金额是多少,选择就是从面额列表 coins 中选择一个零钱,然后目标金额就会减少:
在这里插入图片描述

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        //初始化较大的数,以便于后面min函数计算
        vector<int> dp(amount+1,amount+1);
        dp[0]=0;

        for(auto coin:coins)
        {
            for(int i=1;i<dp.size();i++)
            {
                //子问题无解,跳过
                if(i-coin<0) continue;
                dp[i]=min(dp[i],dp[i-coin]+1);
            }
        }
        return (dp[amount]==amount+1) ? -1:dp[amount];
    }
};

还有Leetcode518. 零钱兑换 II
给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount+1,0);
        dp[0]=1;

        for(auto coin:coins)
        {
            for(int i=1;i<dp.size();i++)
            {
                if(i-coin<0) continue;
                dp[i]+=dp[i-coin];
            }
        }
        return dp[amount];
    }
};

三、考虑物品顺序时,先包再物
Leetcode377. 组合总和 Ⅳ
给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。请注意,顺序不同的序列被视作不同的组合。

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<unsigned long long> dp(target + 1, 0);
        dp[0] = 1;
        for(int i = 0; i <= target; i ++){
            for(auto x:nums){
                if(i - x >= 0) dp[i] += dp[i - x];
            }
        }
        return dp[target];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值