洛谷 P1064 金明的预算方案 分组DP/有依赖DP
总共有 元,买 个物品,每个物品有重要度,并且其中一些物品是捆绑销售的hhh
要使每件物品的价格乘上重要性的总和最大。
思路:有依赖的背包问题,分为主件和附件,对于每个主件,可以选择:
- 不选择主件
- 只选择主件
- 选择主件,然后再选择附件的一种组合
流程大概如下:
首先遍历所有主件,先考虑选择该主件时以及 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;
}