再来一边背包问题 | 完全背包问题

哈喽哈喽这里是小菜不拖延博主,本篇是背包问题的第二篇,主要基于acwing再重温一边完全背包问题,希望能够帮到你~

完全背包问题

完全背包问题在01背包(再来一遍背包问题 | 01背包)的基础上不同的是,每个物品可以被选多次

原题链接:3. 完全背包问题 - AcWing题库

基本逻辑代码

也就是每个物品可以被选k次,如果理解了01背包的话,其实这里也就是又要多一层循环,每个物品被选k次 (基本逻辑现在是会超时的)

c++ 复制代码
#include<iostream>
#include<algorithm>

using namespace std;

const int N=1010;

int n,m;
int v[N], w[N];
int f[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++)
        {
           //每次的k件总体积要小于当前j体积才能装下
            for(int k=0;k*v[i]<=j;k++){
             f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
            }

            
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
}
js 复制代码
function knapsack(n, m, items) {
    let f = Array(n+1).fill().map(() => Array(m+1).fill(0));
    for (let i = 1; i <= n; i++) {
        for (let j = 0; j<=m; j++) {
            for(let k=0;k*v[i]<=j;k++){
            f[i][j] = Math.max(f[i][j], f[i-1][j - items[i][0]] + items[i][1]);
            }
        }
    }
    
    return f[n-1][m];
}

// 示例输入
let n = 4;
let m = 3;
let items = [
    [0,0],
    [1, 2],  // 物品1: 重量 1,价值 2
    [2, 4],  // 物品2: 重量 2,价值 4
    [2, 5],  // 物品3: 重量 3,价值 4
    [4, 5]   // 物品4: 重量 4,价值 5
];

console.log(knapsack(n, m, items));  // 输出最大价值

疑难解释

为什么不再像01背包一样判断是否需要取当前物品了

因为我们加入了参数k,当k等于0的时候就相当于取前i-1个物品,而且我们已经在循环条件中加入了体积判断,就不需要再进行一次判断了,相当于这里进行了小小的优化

k循环优化

c++ 复制代码
#include<iostream>
#include<algorithm>

using namespace std;

const int N=1010;

int n,m;
int v[N], w[N];
int f[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++)
        {
            f[i][j]=f[i-1][j];
            
            if(j>=v[i]) f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);
    

            
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
}
js 复制代码
function knapsack(n, m, items) {
    let f = Array(n+1).fill().map(() => Array(m+1).fill(0));
    for (let i = 1; i <= n; i++) {
        for (let j = 0; j<=m; j++) {
        //不选第i个物品
            f[i][j]=f[i-1][j]
            //选第i个物品,注意这里代码和01不一样
            if(j>=items[i][0]) f[i][j] = Math.max(f[i-1][j], f[i][j - items[i][0]] + items[i][1]);
        }
    }
    
    return f[n-1][m];
}

// 示例输入
let n = 4;
let m = 5;
let items = [
    [0,0],
    [1, 2],  // 物品1: 重量 1,价值 2
    [2, 4],  // 物品2: 重量 2,价值 4
    [3, 4],  // 物品3: 重量 3,价值 4
    [4, 5]   // 物品4: 重量 4,价值 5
];

console.log(knapsack(n, m, items));  // 输出最大价值

注意

和01背包代码不一样!!!,看过01背包的观众老爷会发现,欸,这代码不就是01背包吗。事实上确实相似99.99%,但是恶魔往往藏在细节之中

  • 01背包:f[i][j] = Math.max(f[i-1][j], f[i-1][j - items[i][0]] + items[i][1]);
  • 完全背包:f[i][j] = Math.max(f[i-1][j], f[i][j - items[i][0]] + items[i][1]);

可以看到一个是i-1一个是i。在01背包文章中我们特意强调过这个下标问题,如果是i,那就是基于i,就等于i已经被选过了,恰巧,我们完全背包的情况还真就是i个物品能被选多次(巧了吗这不),所以上一节容易误导的地方恰巧就是完全背包的解法,等于你掌握了01背包就掌握了完全背包!!

解释

如下图,y总写的式子。

  • 第一个式子:当k=1,2,3···时,f[i][j]在选k个i物品的最大值

  • 第二个式子:当k=1,2,3···时,f[i][j-v]在选k个i物品的最大值(也就是把第一个式子k=1时提出来看他的最大值从何而来)

  • 我们会发现每次计算好像都有重复的部分,上下两个式子就差一个w,max(a+w,b+w)==max(a,b)+w,所以第二个式子两边同事加上w,第一二个式子右边黄色部分就相同了,所以替换成第二个式子的左边部分,我们就得到了最终优化后的式子 所以就等于我们不用每次都循环k,只要比上一次多一个w就好了

二维优化成一维

既然01背包能优化到一维,完全背包也一样(经过上面的对比,你是否猜到了代码呢!) 显然完全背包的下标都是i,那我们就可以轻松的直接化成一维了,不用像01背包那样因为下标不同产生额外的变化

c++ 复制代码
#include<iostream>
#include<algorithm>
 
using namespace std;
 
const int N=1010;
 
int n,m;
int v[N], w[N];
int f[N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++)
    {
    //小于v[i]装不下第i个物品,直接过
        for(int j=v[i];j<=m;j++)
        {
           f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
        
    }
    cout<<f[m]<<endl;
    return 0;
}
js 复制代码
function knapsack(n, m, items) {
    let f = Array(m + 1).fill(0);
    for (let i = 1; i <= n; i++) {
        let [weight, value] = items[i];
        for (let j = items[i][0]; j >=weight; j--) {
            f[j] = Math.max(f[j], f[j - weight] + value);
            console.log(f,j,weight)
        }
    }

    return f[m];
}

// 示例输入
let n = 4;
let m = 5;
let items = [
    [0,0],
    [1, 2],  // 物品1: 重量 1,价值 2
    [2, 4],  // 物品2: 重量 2,价值 4
    [3, 4],  // 物品3: 重量 3,价值 4
    [4, 5]   // 物品4: 重量 4,价值 5
];

console.log(knapsack(n, m, items));  // 输出最大价值

练习题目

相关推荐
sjsjs1123 分钟前
【数据结构-表达式解析】【hard】力扣224. 基本计算器
数据结构·算法·leetcode
C++忠实粉丝24 分钟前
计算机网络socket编程(6)_TCP实网络编程现 Command_server
网络·c++·网络协议·tcp/ip·计算机网络·算法
坊钰26 分钟前
【Java 数据结构】时间和空间复杂度
java·开发语言·数据结构·学习·算法
武昌库里写JAVA32 分钟前
一文读懂Redis6的--bigkeys选项源码以及redis-bigkey-online项目介绍
c语言·开发语言·数据结构·算法·二维数组
禊月初三40 分钟前
LeetCode 4.寻找两个中序数组的中位数
c++·算法·leetcode
学习使我飞升1 小时前
spf算法、三类LSA、区间防环路机制/规则、虚连接
服务器·网络·算法·智能路由器
庞传奇1 小时前
【LC】560. 和为 K 的子数组
java·算法·leetcode
SoraLuna2 小时前
「Mac玩转仓颉内测版32」基础篇12 - Cangjie中的变量操作与类型管理
开发语言·算法·macos·cangjie
daiyang123...2 小时前
Java 复习 【知识改变命运】第九章
java·开发语言·算法
田梓燊3 小时前
湘潭大学软件工程算法设计与分析考试复习笔记(六)
笔记·算法·软件工程