[Acwing]1019.庆功会 原创

算法标签 多重背包问题

题目简叙

处理这个问题之前,我们先了解一下多重背包问题
在这里插入图片描述

解析

你可以把它理解为01背包问题或者完全背包问题的变体
唯一的区别就是在两者基础上加入了一个东西能放多少个,所以我要加入一个循环

#include<iostream>

using namespace std;

const int N=1e2+10;
int f[N][N];
int v[N],w[N],s[N];

int main()
{
    int n,m;
    cin>>n>>m;
        
    for(int i=1;i<=n;i++)cin>>v[i]>>w[i]>>s[i];//s 一个物品最多能获取多少个
    
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
            for(int k=0;k*v[i]<=j&&k<=s[i];k++)//我们挑取K个第i个物品,且总重量不能大于s[i]
                f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);//我们挑取K个第i个物品,可以挑选多少个


    
    int res=1;
    for(int i=0;i<=m;i++)res=max(res,f[n][i]);//在同样挑选完毕 但是重量不同的方案中得到最佳答案
    cout<<res;
    
    return 0;
}

在这里插入图片描述

优化
优化逻辑参照我前两题写的

Q:为什么这里的 注意点1 需要从大到小循环?
A:因为我们压缩的了代码,将二维降低到了一维,f[i, j] = max(f[i - 1, j], f[i - 1, j - v] + w,那么只能从大到小循环,否则计算j时,j - v会先被计算,那么其实算的就是f[i, j] = max(f[i - 1, j], f[i], j - v] + w了,不等价。

#include<iostream>

using namespace std;

const int N=1e2+10;
int f[N];

int main()
{
    int n,m;
    cin>>n>>m;
    
    int v,w,s;
    for(int i=1;i<=n;i++)
    {
        cin>>v>>w>>s;
        for(int j=m;j>=0;j--)//注意点1
            for(int k=0;k*v<=j&&k<=s;k++) 
                f[j]=max(f[j],f[j-k*v]+k*w);
    }


    
    cout<<f[m];
    
    return 0;
}

在这里插入图片描述

现在我们来查看庆功会这道题
在这里插入图片描述
很明显是多重背包的裸题

我们直接利用上面优化出来的方式解决这道题目

#include<iostream>

using namespace std;

const int N = 6e3+10;
int f[N];

int main()
{
    int n,m;
    cin>>n>>m;
    
    int v,w,s;
    for(int i=0;i<n;i++)
        {
            cin>>v>>w>>s;
            for(int j=m;j>=0;j--)
                for(int k=0;k*v<=j&&k<=s;k++)
                    f[j]=max(f[j],f[j-k*v]+k*w);
        }
    
    cout<<f[m];
    
    return 0;
}

在这里插入图片描述

posted @ 2022-06-26 22:07  俺叫西西弗斯  阅读(0)  评论(0)    收藏  举报  来源