【题解】P1064 [NOIP2006 提高组] 金明的预算方案
与传统01背包不同的是,每个主件不再是只有选/不选两种情况,由于还带有0/1/2个附件,所以每个主件一共有如下种情况:
没有附件:
不选主件/选主件(价格允许的话)
有一个附件:
不选主件/选主件(价格允许的话)/选主件和附件(价格允许选二者的话)
有两个附件:
不选主件/选主件(价格允许的话)/选主件和附件A(价格允许选二者的话)/选主件和附件B(价格允许选二者的话)/选主件和附件A和附件B(价格允许选二者的话)
这个题的特性是附件的数量少,只有0/1/2个,因此我们可以把附件涵盖在主件内处理而不用单独考虑附件的选择条件。
坑点:由于附件不单独处理,所以如果开二维数组进行DP每次沿用上一行的状态的话会调用到附件的空状态。所以应该直接开一维数组然后倒序枚举总钱数。
优化:由于物品价格都是10的倍数所以一开始就把n和v同时除以10最后总答案输出时再乘上10即可。
int n, m, q;
struct furniture{
int v, p, ifmain;
vi fujian;
}fur[66];
ll f[32010];
int main(){
n = rd(); m = rd(); n /= 10;
for(int i = 1; i <= m; ++i){
fur[i].v = rd(); fur[i].v /= 10;
fur[i].p = rd();
q = rd();
if(q != 0){
fur[q].fujian.pb(i);
fur[i].ifmain = 0;
}
else fur[i].ifmain = 1;
}
for(int i = 1; i <= m; ++i){
if(!fur[i].ifmain) continue;
for(int j = n; j >= 1; --j){
if(j < fur[i].v) continue;
int kind = fur[i].fujian.size();
if(kind >= 0){
f[j] = max(f[j], f[j - fur[i].v] + fur[i].v * fur[i].p);
}
if(kind >= 1){
if(j >= fur[i].v + fur[fur[i].fujian[0]].v)
f[j] = max(f[j], f[j - fur[i].v - fur[fur[i].fujian[0]].v] + fur[i].v * fur[i].p + fur[fur[i].fujian[0]].v * fur[fur[i].fujian[0]].p);
}
if(kind >= 2){
if(j >= fur[i].v + fur[fur[i].fujian[1]].v)
f[j] = max(f[j], f[j - fur[i].v - fur[fur[i].fujian[1]].v] + fur[i].v * fur[i].p + fur[fur[i].fujian[1]].v * fur[fur[i].fujian[1]].p);
if(j >= fur[i].v + fur[fur[i].fujian[0]].v + fur[fur[i].fujian[1]].v)
f[j] = max(f[j], f[j - fur[i].v - fur[fur[i].fujian[0]].v - fur[fur[i].fujian[1]].v] + fur[i].v * fur[i].p + fur[fur[i].fujian[0]].v * fur[fur[i].fujian[0]].p + fur[fur[i].fujian[1]].v * fur[fur[i].fujian[1]].p);
}
}
}
printf("%lld\n", f[n] * 10);
return 0;
}