题意

  • 给定n个物品,每个物品由价值和体积,问装入体积为v的背包的最大价值和恰好装满的最大价值

思路

  • 01背包问题,对于每个物品考虑装和不装即可,如果不装\装不下 ,如果装则考虑装进来后的价值 ,对于每一个物品,在装和不装里取最大即可

  • 注意到,n和val最大都可以取1000,二维数组可以开 ,也可以考虑就地滚动进行优化,那么,每次遍历体积时,为了防止重复更新,需要从v遍历到vol[i](倒序遍历),同时,由于转移只发生在一层数组中,所以每次更新变为和原值比较最大的

  • 初值:对于第一个问题,所有位置初值赋为0即可(可以视作有价值为0体积任意的空气最初填满背包),由于放入每个物品时都会自动从能放入的地方开始到v全部放入,所以每层背包都会保证单增, 即为所求答案。

    对于第二个问题,因为要求恰好填满,所以每一个恰好填满的答案一定是从 开始往后转移的,而不是 开始,所以,除了 赋值为0以外,其他位置都赋一个极小的值(0x3f3f3f3f),最后,如果 直接输出结果,如果 说明没有可以从0转移过来的路径,输出0。

AC代码

#include<bits/stdc++.h>
using namespace std;
int dp1[101010];
int dp2[101010];
int vol[1010],val[1010];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);

    int v,n;
    cin >> n >> v ;
    for(int i=1;i<=n;i++){
        cin >> vol[i] >> val[i] ;
    }
    //v2:就地滚动
    for(int i=1;i<=n;i++){
        for(int j=v;j>=vol[i];j--)
            dp1[j]=max(dp1[j],dp1[j-vol[i]]+val[i]);
    }
    memset(dp2,-0x3f3f3f3f,sizeof(dp2));
    dp2[0]=0;
    for(int i=1;i<=n;i++){
        for(int j=v;j>=vol[i];j--)
            dp2[j]=max(dp2[j],dp2[j-vol[i]]+val[i]);
    }
    cout << dp1[v] << '\n' << max(0,dp2[v]) << '\n';
    return 0;
}