题解【AcWing487】金明的预算方案

题面

有依赖的背包问题模板题。

我们观察到 每个主件可以有 0 个、1 个或 2 个附件

于是考虑对于每一个主件,我们用枚举子集的方式枚举使用哪一些附件,

然后就是一个经典的分组背包问题了。

注意背包问题一般是先枚举物品,再枚举体积,最后枚举决策。

#include <bits/stdc++.h>

using namespace std;

typedef pair <int, int> PII;
typedef pair <int, PII> PIII;

int n, m, ans, dp[32003];
vector <PIII> mas; //存储每一个主件
vector <PII> att[66]; //存储每一个主件的附件

int main()
{
    cin >> m >> n;
    //m 为最大价格,n 为物品个数
    for (int i = 1; i <= n; i+=1)
    {
        int v, w, q;
        cin >> v >> w >> q;
        if (!q) 
            mas.push_back(make_pair(i, make_pair(v, v * w))); //这个物品是主件,就保存它的编号、价格和价值
        else 
            att[q].push_back(make_pair(v, v * w)); //附件就存储它的价格和价值
    }
    int fst = mas.size(); //有多少个主件
    for (int i = 0; i < fst; i+=1) //枚举每一个主件(分组背包问题中的组数)
    {
        for (int j = m; j >= 0; j-=1) //枚举体积
        {
            int bh = mas[i].first, jiage = mas[i].second.first, jiazhi = mas[i].second.second;
            //分别存储当前枚举到的主件的编号、价格和价值
            int fj_gs = att[bh].size(); //当前主件的附件个数
            for (int k = 0; k < (1 << fj_gs); k+=1) //枚举子集
            {
                int v = jiage, w = jiazhi; //存储现有的价格和价值
                for (int l = 0; l < fj_gs; l+=1) //枚举每个附件
                    if (k >> l & 1) //如果要选择这个附件
                        v += att[bh][l].first, w += att[bh][l].second; //加上这个附件的价值和价格
                if (j >= v) dp[j] = max(dp[j], dp[j - v] + w); //这种方案合法就进行转移
            }
        }
    }
    cout << dp[m] << endl; //输出最大价值
    return 0;
}
posted @ 2020-02-20 15:22  csxsi  阅读(133)  评论(0编辑  收藏  举报