https://www.luogu.org/problemnew/show/P1064

题解:

带有附件的背包问题,它属于01背包的变式。

这题还好,每一个物品最多只有两个附件,那么我们在对主件进行背包的时候,决策就不再是两个了,而是五个。

还记得01背包的决策是什么吗?

1.不选,然后去考虑下一个

2.选,背包容量减掉那个重量,总值加上那个价值。

这个题的决策是五个,分别是:

1.不选,然后去考虑下一个

2.选且只选这个主件

3.选这个主件,并且选附件1

4.选这个主件,并且选附件2

5.选这个主件,并且选附件1和附件2.

这个。。。很好想吧。。。

我们知道,01背包的状态转移方程(已使用滚动数组优化)是f[j] = max(f[j],f[j-w[i]]+c[i]),那么,这道题的转移方程也就不难写出了。

等等,你得先判断某个选附件的决策是不是可行的,如果当前的容量还够放第一个,或第二个,或两个都选的附件,那么才能考虑转移。

当然,不选附件的话就不用判啦,直接01背包的转移方程即可。

我们令main_item_w数组表示某个主件的费用,而main_item_c数组表示某个主件的价值。

同样的,用二维数组annex_item_w表示某个附件的费用,annex_item_c表示某个附件的价值,第二维只需要0,1,2这三个数,其中第二维是0的场合表示这个主件i的附件数量,它只能等于0或1或2。第二维是1或者是2的值代表以i为主件的附件1或者附件2的相关信息(费用 价值)。这些数组的信息应该在读入时处理好,具体详见代码。

这样,状态转移方程就是四个。

不选附件的①:f[j] = max(f[j],f[j-main_item_w[i]]+main_item_c[i]);

选附件1的②:f[j] = max(f[j],f[ j - main_item_w[i] - annex_item_w[i][1] ] + main_item_c[i] + annex_item_c[i][1]);

选附件2的③:f[j] = max(f[j],f[ j - main_item_w[i] - annex_item_w[i][2] ] + main_item_c[i] + annex_item_c[i][2]);

选附件1和附件2的④:f[j] = max(f[j],f[ j - main_item_w[i] - annex_item_w[i][1] - annex_item_w[i][2] ] + main_item_c[i] + annex_item_c[i][1] + annex_item_c[i][2]);

已经滚动掉了第一维,道理和正常向的01背包都是一样的,即只有i和i-1有关系,但是这个规律在循环中已经满足了所以完全没必要记录。

目标状态f[n],输出就好。

/*
*@Author:   STZG
*@Language: C++
*/
#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
using namespace std;
typedef long long ll;
typedef __int128 lll;
const int N=32000+1000;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
int t,n,m,k,q,v,p;
int mw[N];
int mc[N];
int aw[N][3];
int ac[N][3];
int f[N];
int main()
{
#ifdef DEBUG
	freopen("input.in", "r", stdin);
	//freopen("output.out", "w", stdout);
#endif
    cin >> n >> m;
    for (int i=1;i<=m;i++){
        cin >> v >> p >> q;
        if (!q){
            mw[i] = v;
            mc[i] = v * p;
        }
        else{
            aw[q][0]++;
            aw[q][aw[q][0]] = v;
            ac[q][aw[q][0]] = v * p;
        }
    }

    for (int i=1;i<=m;i++)
        for (int j=n;mw[i]!=0 && j>=mw[i];j--){
            f[j] = max(f[j],f[j-mw[i]]+mc[i]);

            if (j >= mw[i] + aw[i][1])
                f[j] = max(f[j],f[ j - mw[i] - aw[i][1] ] + mc[i] + ac[i][1]);

            if (j >= mw[i] + aw[i][2])
                f[j] = max(f[j],f[ j - mw[i] - aw[i][2] ] + mc[i] + ac[i][2]);

            if (j >= mw[i] + aw[i][1] + aw[i][2])
                f[j] = max(f[j],f[ j - mw[i] - aw[i][1] - aw[i][2] ] + mc[i] + ac[i][1] + ac[i][2]);

         }
     cout << f[n] << endl;
    //cout << "Hello world!" << endl;
    return 0;
}