dp(十一)填满背包方案数

目录

LintCode 炼码物品大小不重复的填满背包的方案数

 LintCode 炼码 物品大小存在重复情况

 LintCode 炼码不重复的组合个数

完全背包_牛客题霸_牛客网


 


LintCode 炼码物品大小不重复的填满背包的方案数

描述给出 n 个物品, 以及一个数组, nums[i]代表第i个物品的大小, 保证大小均为正数并且没有重复, 正整数 target 表示背包的大小, 找到能填满背包的方案数。
每一个物品可以使用无数次(完全背包由前往后)

class Solution {
public:
    int backPackIV(vector<int> &nums, int target) {
        // write your code here
        int n = nums.size();
        vector<int> dp(target+1);
        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];
    }
};

 LintCode 炼码 物品大小存在重复情况

给出 n 个物品, 以及一个数组, nums[i] 代表第i个物品的大小, 保证大小均为正数, 正整数 target 表示背包的大小, 找到能填满背包的方案数。
每一个物品只能使用一次(0-1背包由后往前)

class Solution {
public:
    /**
     * @param nums: an integer array and all positive numbers
     * @param target: An integer
     * @return: An integer
     */
    int backPackV(vector<int> &nums, int target) {
        // Write your code here
        vector<int> dp(target + 1);
        dp[0] = 1;
        for (auto a : nums) {
            for (int i = target; i >= a; --i) {
                dp[i] += dp[i - a];
            }
        }
        return dp.back();
    }
};

 LintCode 炼码不重复的组合个数

class Solution {
public:
    int backPackVI(vector<int> &nums, int target) {
        // write your code here
        vector<int> dp(target+1);
        dp[0] =1;
        for (int i = 1; i <= target; i++) {
            for (int num : nums) {
                if (num <= i) {
                    dp[i] += dp[i - num];
                }
            }
        }
        return dp[target];
    }
};

 


【模板】01背包_牛客题霸_牛客网

描述

你有一个背包,最多能容纳的体积是V。

现在有n个物品,第i个物品的体积为vivi​ ,价值为wiwi​。

(1)求这个背包至多能装多大价值的物品?

(2)若背包恰好装满,求至多能装多大价值的物品?

输入描述:

第一行两个整数n和V,表示物品个数和背包体积。

接下来n行,每行两个数vivi​和wiwi​,表示第i个物品的体积和价值。

1≤n,V,vi,wi≤10001≤n,V,vi​,wi​≤1000

输出描述:

输出有两行,第一行输出第一问的答案,第二行输出第二问的答案,如果无解请输出0。

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

int A[1005];
int w[1005];

int main() 
{
    int n,m;
    cin>>n>>m;
    for(int i = 0; i < n; i++)
        cin>>A[i]>>w[i];
    vector<int> a(m+1, 0);
    vector<int> b(m+1, -0x3f3f3f3f);
    b[0] = 0;
    for(int i = 0; i < n; i++)
    {
        for(int j = m; j >=A[i]; j--)
        {
            a[j] = max(a[j], a[j-A[i]]+w[i]);
            b[j] = max(b[j], b[j-A[i]]+w[i]);
        }
    }
    cout<<a[m]<<endl;
    b[m] = b[m] > 0 ? b[m] : 0;
    cout<<b[m]<<endl;
    return 0;
}


完全背包_牛客题霸_牛客网

描述

你有一个背包,最多能容纳的体积是V。

现在有n种物品,每种物品有任意多个,第i种物品的体积为vivi​ ,价值为wiwi​。

(1)求这个背包至多能装多大价值的物品?

(2)若背包恰好装满,求至多能装多大价值的物品?

数据范围: 1≤v,vi,wi≤1000 1≤v,vi​,wi​≤1000 

示例1

输入:6,2,[[5,10],[3,1]]

返回值:[10,2]

示例2

输入:8,3,[[3,10],[9,1],[10,1]]

返回值:[20,0]

说明:无法恰好装满背包。

示例3

输入:13,6,[[13,189],[17,360],[19,870],[14,184],[6,298],[16,242]]

返回值:[596,189]

说明:可以装5号物品2个,达到最大价值298*2=596,若要求恰好装满,只能装1个1号物品,价值为189.

问题1和问题2的求解过程基本一致 ,不同的是在动态规划初始化数组时,在求解问题1时其所对应的动态规划数组全部为0,在求解问题2时其所对应的动态规划数组只有第一个元素为0其余的为负无穷。之所以将动态规划数组里的元素设为负无穷,是为了进行阻断。在从前至后推进时如果在填充了当前元素后还有剩余空间,那么之前扫描过的其他元素若不能恰好填满剩余空间,则这个元素将无法被成功填充(表征为:负无穷加上一个常数还是负无穷),即这种情况将会被阻断。

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param v int整型 
     * @param n int整型 
     * @param nums int整型vector<vector<>> 
     * @return int整型vector
     */
    vector<int> knapsack(int m, int n, vector<vector<int> >& nums) {
        // write code here
        vector<int> dp(m+1, 0);
        vector<int> b(m+1, INT_MIN);
        b[0] = 0;
        vector<int> v(n);
        vector<int> w(n);
        for(int i = 0; i < n; ++i)
        {
            v[i] = (nums[i][0]);
            w[i] = (nums[i][1]);
        }
        for(int i = 0; i < n; ++i)
        {
            for(int j = v[i]; j <= m; j++)
            {
                dp[j] = max(dp[j], dp[j-v[i]]+w[i]);
                b[j] = max(b[j], b[j-v[i]]+w[i]);
            }
        }
        vector<int> res(2);
        res[0] = dp[m];
        res[1] = b[m]>0?b[m]:0;
        return res;
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值