dp(十)* 0-1背包与完全背包的组合数问题 && 搞清组合数与排列数

目录

兑换零钱(二)_牛客题霸_牛客网(完全背包)

494. 目标和(0-1背包)

分割等和子集_牛客题霸_牛客网


兑换零钱(二)_牛客题霸_牛客网(完全背包)

描述

给定一个整数数组 nums 表示不同数额的硬币和一个正整数 target 表示总金额,请你计算并返回可以凑出总金额的的组合数。如果凑不出 target 则返回 0。

状态转移方程的推导就是利用上面,发现当背包容量为1的时候有dp[4]种方法可以凑出容量为5的方法数……最后发现dp[5]就等于其他dp[j-nums[i]]之和

初始化要设置第一个元素为1,比如

    int change(int target, vector<int>& nums) {
        // write code here
        int n = nums.size();
        vector<int> dp(target+1, 0);
        dp[0] = 1;
        for(int i = 0; i < n; ++i)    // 物品
        {
            for(int j = nums[i]; j <= target; ++j)    // 背包
            {
                dp[j] += dp[j-nums[i]];
            }
        }
        return dp[target];
    }

总结:

        1、判断是01背包还是完全背包

        2、组合数的状态转移方程都为dp[j] += dp[j-nums[i]]

        3、如果求的是组合数就先进行遍历物品,再遍历背包

             (因为物品从前到后进行拿取,有着顺序,不会重复)

              如果求得是排列数先遍历背包在遍历物品

494. 目标和(0-1背包)

给你一个整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :

    例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。

返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/target-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

示例 1:

输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/target-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

 1、判断是01背包问题 (从后往前)

2、组合数使用 dp[j] += dp[j-nums[i]];

3、不同的组合数 (则使用先物品再背包)

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int add = 0;
        for(auto &e : nums)
        {
            add += e;
        }
        if(add+target<0 || (add+target)%2 == 1)return 0;
        int left = (add+target)/2;
        vector<int> dp(left+1, 0);
        dp[0] = 1;
        for(int i = 0; i < nums.size(); ++i)
        {
            for(int j = left; j >= nums[i]; --j)
            {
                dp[j] += dp[j-nums[i]];
            }
        }
        return dp[left];
    }
};

分割等和子集_牛客题霸_牛客网

描述

给定一个只包含正整数的数组 nums ,请问能否把这个数组取出若干个数使得取出的数之和和剩下的数之和相同。

数据范围: 1≤n≤500 1≤n≤500  , 数组中的元素满足 1≤numsi≤100 1≤numsi​≤100 

输入描述:

第一行输入一个正整数 n ,表示数组 nums 的长度。

第二行输入 n 个正整数,表示数组中的值。

输出描述:

如果满足题目条件,输出 true ,否则输出 false

1、0-1背包问题

2、dp[j] += dp[j-nums[i]]

3、重不重复无所谓,先背包先物品都行

#include <iostream>
using namespace std;
#include<vector>

int main() {
    int n;
    cin>>n;
    vector<int> nums(n);
    for(int i = 0; i < n; i++)cin>>nums[i];

    int add = 0;
    for(auto &e : nums)
    {
        add+=e;
    }
    if(add%2==1)
    {
        cout<<"false"<<endl;
        return 0;
    }
    int left = add/2;
    vector<int> dp(left+1);
    dp[0] = 1;
    for(int i = 0; i < nums.size(); i++)
    {
        for(int j = left; j >= nums[i]; j--)
        {
            dp[j] += dp[j-nums[i]];
        }
    }
    if(dp[left]==0)
        cout<<"false"<<endl;
    else
        cout<<"true"<<endl;
    return 0;
}
// 64 位输出请用 printf("%lld")
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值