背包问题学习笔记-混合背包问题

news/2025/1/15 16:02:36/

题意描述:

有 N 种物品和一个容量是 V 的背包。物品一共有三类:第一类物品只能用1次(01背包);
第二类物品可以用无限次(完全背包);
第三类物品最多只能用 si 次(多重背包);每种体积是 vi,价值是 wi。求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。si=−1 表示第 i 种物品只能用1次;
si=0 表示第 i 种物品可以用无限次;
si>0 表示第 i 种物品可以使用 si 次;输出格式
输出一个整数,表示最大价值。数据范围
0<N,V≤1000
0<vi,wi≤1000
−1≤si≤1000

示例:

4 5
1 2 -1
2 4 1
3 4 0
4 5 2

8


解题思路:
Alice: 笑死🤣,这题可以直接改一下输入,改成多重背包就可以了
Bob: 对,01背包的物品数量就是 1,数量 1 也是多重背包的一种。完全背包的话,背包体积是有限的,能装进去的物品数量也是有限的,直接算一下,然后上取整就得了。其他代码不用改,直接用单调队列优化的肯定能过。


代码:

转换成多重背包 + 单调队列求解

const fs = require('fs');
let buffer = '';process.stdin.on('readable', () => {const chunk = process.stdin.read();if (chunk) {buffer += chunk.toString()}
});// 输入的字符串转换为数字
const convert = (inputString) => {const list = [];inputString.split('\n').forEach((line) => {const tokens = line.split(' ');list.push(tokens.map(num => parseInt(num, 10)));});return list;
}// 批量调用
const batchCall = (list, solve) => {// 划分数据const data = [];let countAndVolumIndex = 0;while(countAndVolumIndex < list.length) {const [count, volum] = list[countAndVolumIndex];// 转化成多重背包问题data.push({volum: volum,count: count,volumAndWeightAndCount: list.slice(countAndVolumIndex + 1, countAndVolumIndex + 1 + count).map(item => {const [v, w, s] = item;let normalizedS = s;if(s === -1) {normalizedS = 1;}if(s === 0) {normalizedS = Math.ceil(volum / v);}return [v, w, normalizedS];}),});countAndVolumIndex += count + 1;}data.forEach(item => {if(solve && item && item.count && item.volum) {solve(item.count, item.volum, item.volumAndWeightAndCount);}});
}const solve = (count, maxVolum, volumAndWeightAndCount) => {// 单调队列优化方法const dp = new Array(maxVolum + 10).fill(0);// 对于每种物品 for (let i=0; i<count; ++i) {// 状态压缩const lastRowDp = [...dp];// 取出第 i 种物品的体积,价值,数量const [vi, wi, si] = volumAndWeightAndCount[i];// 对于每种可能剩余的体积,0,1,2, ... vi-1 for (let r=0; r<vi; ++r) {// 单调队列求解每种可能的最大值,滑动窗口大小是,math.min (si, maxVolum / vi) 下取整// 0 + 0v, 0 + 1v, 0 + 2v ... 0 + kv 的数组中滑动,每次一步// 最大价值对应的体积的单调队列,双端队列,queue[0] 是合法的最大价值对应的体积const queue = [];for(let j=r; j<=maxVolum; j+=vi) {// 维护队首// i 物品的体积超了while(queue.length && j-queue[0] > vi*si) {queue.shift();}// 维护队尾while(queue.length && lastRowDp[queue[queue.length-1]] + (j - queue[queue.length-1]) /vi * wi <= lastRowDp[j]) {queue.pop();}// 入队queue.push(j);// 更新 dpdp[j] = lastRowDp[queue[0]] + (j-queue[0]) / vi * wi;}}}console.log(dp[maxVolum]);
}process.stdin.on('end', function() {batchCall(convert(buffer), solve)
});

参考:

  • 题目链接

http://www.ppmy.cn/news/1145781.html

相关文章

堆栈模拟队列

设已知有两个堆栈S1和S2&#xff0c;请用这两个堆栈模拟出一个队列Q。 所谓用堆栈模拟队列&#xff0c;实际上就是通过调用堆栈的下列操作函数: int IsFull(Stack S)&#xff1a;判断堆栈S是否已满&#xff0c;返回1或0&#xff1b; int IsEmpty (Stack S )&#xff1a;判断堆…

闭包(C#)

通常来讲&#xff0c;大家一听到闭包&#xff0c;应该首先会想到JavaScript中的闭包&#xff0c;而不会想到C#中的闭包&#xff0c;但是C#中也是有闭包的&#xff0c;下面就让我来为大家仔细讲解讲解。 在C#中&#xff0c;我们通常知道变量作用域有三种&#xff1a;1、是属于类…

FaceFusion:探索无限创意,创造独一无二的面孔融合艺术!

FaceFusion&#xff1a;探索无限创意&#xff0c;创造独一无二的面孔融合艺术&#xff01; 它使用先进的图像处理技术&#xff0c;允许用户将不同的面部特征融合在一起&#xff0c;创造有趣和令人印象深刻的效果。这个项目的潜在应用包括娱乐、虚拟化妆和艺术创作&#xff0c;…

力扣 -- 132. 分割回文串 II

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:int minCut(string s) {int ns.size();//保存s的所有子串是否是回文串的信息的哈希表vector<vector<bool>> hash(n,vector<bool>(n));for(int in-1;i>0;i--){for(int ji;j<n;j){i…

Qt QGridLayout和QFormLayout案例分析

QGridLayout和QFormLayout是Qt中常用的布局管理器&#xff0c;可以用于在应用程序中设置控件的位置和大小。 QGridLayout网格布局(栅格布局) QGridLayout是一个网格布局管理器&#xff0c;可以将控件放置在一个二维网格中。在QGridLayout中&#xff0c;控件可以跨越多个行和列…

GIS与人工智能应用结合举例(100例)

目录 一.地理信息智能导航与交通1 基于地理信息的智能导航系统。2 智能交通管理与优化。3 GIS在交通规划和流量优化中的应用。4 智能公交与交通管理。5 智能交通信号控制。6 智能公共交通系统中的地理信息技术应用。7 GIS在物流和货运路径优化中的应用。 二.地理信息在环境监测…

波奇学C++:哈希

哈希本质是的值和位置建立关联起来&#xff0c;这种关联关系就是哈希函数 示例:除留余数&#xff1a;对输入的数字取模。 哈希冲突&#xff1a;多个不同的值指向同一个位置 解决方法&#xff1a; 闭散列&#xff1a;开发地址法。 把24放在下一个位置 哈希桶 闭散列法 闭散列…

20哈希表-三数之和

目录 LeetCode之路——15. 三数之和 分析&#xff1a; 官方题解&#xff1a; LeetCode之路——15. 三数之和 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nu…