// 最近才发现洛谷也有整理好的分类题集,于是前来加强练习一番。
在普及练习场分分钟解决了动态规划的背包问题,最后卡在 -->
P1064 金明的预算方案
重学了一遍分组背包终于解决了该问题。 参考背包问题九讲
注意分组后不能重复选啊!!!
解题思路
将附件做01背包求得在不同金钱下购买若干附件获得的最大价值,将所有方案加入到主件。现在新的主件中只能选择购买其中一种,于是转化为分组背包问题。
AC代码
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
struct Node {
int v, p; // 花费 与 价值
};
vector<Node> Main[64];
vector<Node> Part[64];
int dp[33000];
int main() {
int V, m;
cin>>V>>m;
for(int i=1;i<=m;i++) {
int v, p, q;
scanf("%d %d %d", &v, &p, &q);
p *= v;
if(!q) Main[i].push_back((Node){v, p});
else
Part[q].push_back((Node){v, p});
}
for(int i=1;i<=m;i++) {
if(Part[i].size()==0) continue;
// i主件的j附件 01背包
memset(dp, 0, sizeof(dp));
for(int j=0;j<Part[i].size();j++) {
int v = Part[i][j].v, p = Part[i][j].p;
for(int k=V;k>=v;k--) {
dp[k] = max(dp[k], dp[k-v]+p);
}
}
// 金钱j+Main[i][0].v 最多获得 dp[j]+Main[i][0].p
// 全部放入Main分组
int last = 0;
for(int j=1;j+Main[i][0].v<=V;j++) {
if(dp[j]>last)
Main[i].push_back((Node){j+Main[i][0].v, dp[j]+Main[i][0].p}), last = dp[j];
}
}
// 分组背包
memset(dp, 0, sizeof(dp));
for(int i=1;i<=m;i++) {
for(int k=V;k>=0;k--) {
for(int j=0;j<Main[i].size();j++) {
int v = Main[i][j].v, p = Main[i][j].p;
if(k>=v)
dp[k] = max(dp[k], dp[k-v]+p);
}
}
}
printf("%d\n", dp[V]);
return 0;
}