洛谷 P1064 金明的预算方案 分组DP/有依赖DP

总共有 nn 元,买 mm 个物品,每个物品有重要度,并且其中一些物品是捆绑销售的hhh

要使每件物品的价格乘上重要性的总和最大。

思路:有依赖的背包问题,分为主件和附件,对于每个主件,可以选择:

  1. 不选择主件
  2. 只选择主件
  3. 选择主件,然后再选择附件的一种组合

流程大概如下:

首先遍历所有主件,先考虑选择该主件时以及 0 到多个附件时的每个价格的最优解(01背包)

然后再将这些价位与不选择该主件时的情况取最优解

详见代码及注释

#include<iostream>
#include<cstdio>
#include<algorithm>
#define MAXN 40000
#define MAXM 70
using namespace std;
int n,m,p[MAXM],v[MAXM],q[MAXM],h[MAXN],f[MAXN];
int main(){
#ifdef WINE
    freopen("data.in","r",stdin);
#endif
    scanf("%d%d",&n,&m); // 总钱数,总个数
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&p[i],&v[i],&q[i]);
        v[i]*=p[i]; // 价格乘重要度
    }
    // f[n] 表示花费 n 得到的最大值
    for(int i=1;i<=m;i++){
        if(!q[i]){ // 主件
            for(int j=1;j<p[i];j++)h[j]=0;
            for(int j=p[i];j<=n;j++)
                h[j]=f[j-p[i]]+v[i]; // 此处 h 存放了购买当前主件可以获得的值
            for(int j=1;j<=m;j++)
                if(q[j]==i) // 是当前主件的附件,所以相当于枚举该主件的附件
                    for(int k=n;k>=p[i]+p[j];k--) // 01背包,h 已经买了当前主件
                        h[k]=max(h[k],h[k-p[j]]+v[j]); // 此处是再购买当前附件
            // 到这里,h 存的是购买当前主件,然后对附件进行任意选择后得到的最大值
            // 而原来的 f 中没有购买当前主件
            for(int j=p[i];j<=n;j++)
                f[j]=max(f[j],h[j]); // 所以此处是对是否购买当前主件进行选择
            // 总结:
            // 对于一个主件,首先用 01背包 选出来怎么搭配附件的最优解,此处的前提都是购买主件
            // 然后再与不购买主件进行最优选择
        }
    }
    printf("%d\n",f[n]);
    return 0;
}

posted @ 2020-06-19 15:49  winechord  阅读(86)  评论(0编辑  收藏  举报