限制物品件数的背包问题

先看一道例题

w

wn(n<=500)m(m<=6000)nm

wn3vwsI0s

v<=100w<=1000s<=10w


1000

80 24

459

357

436

221

1040

 

分析一下:

 

分析一下这个问题。我们首先要找状态的表示。啊,这个问题显然基于一般的01背包问题和完全背包问题,
所以可以用dp[i]表示当所用的价格为i时,最大的价值为dp[i]。
状态转移方程为dp[i] = max(dp[i],dp[i-w[i]]+v[i])
然后再考虑把对于数量的限制加入到状态转移方程中。
根据某大佬的告诫:多一个限制条件基本上(不是一定!!)就要把数组多开一维,或者要多加一层循环(也不是一定!!)。
所以我们能想到多开一维存储已经每件物品用过的件数。
啊,然后就能发现这种转移空间复杂度特别大,根本不可能实现。
因此我们就可以考虑多开一层循环枚举每个物品每次取的个数k。(以上大部分都是废话)

核心:(1)多一个限制条件基本上(不是一定!!)就要把数组多开一维,或者要多加一层循环(也不是一定!!)

  (2)这道题就是多加一层循环

 

emmm。。。这道题还有一部分小小的细节和特判

#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
    int n, m, dp[100004] = {}, savw[10004] = {}, savv[10004] = {}, savs[10004] = {};
    scanf("%d%d", &n, &m);
    for(int i = 1;i <= n;i++)
    {
        scanf("%d%d%d", &savw[i],&savv[i],&savs[i]); 
    }
    for(int i = 1;i <= n;i++)
    {
    /*    for(int k = 1;k <= savs[i];k++)
        {
            int dis = k * savw[i];
            for(int j = m;j >= dis;j--)
            {
                dp[j] = max(dp[j],dp[j-dis]+k*savv[i]);    
            }
        }*/
        for(int j = m;j >= 0;j--)//这里的j应该在k之外,这是为了重复加上k件,导致每件物品实际选的次数超过限制次数savs【i】
        {
            for(int k = 1;k <= savs[i];k++)
            {
                if(k*savw[i] <= j)
                {
                    dp[j] = max(dp[j],dp[j-k*savw[i]]+k*savv[i]);
                }
            }
        }
    }
    printf("%d", dp[m]);
    return 0;    
}

 

posted @ 2021-05-18 20:30  Mint-hexagram  阅读(373)  评论(0编辑  收藏  举报