【Tai_mount】 算法学习 - 动态规划 - luoguP1064 [NOIP2006 提高组] 金明的预算方案
题目地址:https://www.luogu.com.cn/problem/P1064
纪念:人生中第一道绿题,且解决得很迅速,思路顺畅
果然其实是我太菜了,都16了才做个绿题。
好吧进入正题,这道题不太难,思路easy,代码量稍微有点,首先先把代码摆出来。
//https://www.luogu.com.cn/problem/P1064
#include<iostream>
using namespace std;
const int N=67,M=32007;
int n,m,dp[M],grp_num;
struct Objs{
int v=0;
int w=0;
int blg=0;
}obj[N];
struct Groups{
int main;
int off[N];
int num=0;
}group[N];
void input(){
cin>>m>>n;
for(int i=1;i<=n;i++){
cin>>obj[i].v>>obj[i].w>>obj[i].blg;
if(obj[i].blg==0){
grp_num++;
group[grp_num].main=i;
}
}
}
void init(){
for(int i=1;i<=n;i++){
if(obj[i].blg!=0){
int blggrp;
for(int j=1;j<=grp_num;j++){
if(group[j].main==obj[i].blg)
blggrp=j;
}
group[blggrp].num++;
group[blggrp].off[group[blggrp].num]=i;
}
}
for(int i=1;i<=grp_num;i++){
group[i].off[0]=0;
}
}
void dpfun(){
for(int i=1;i<=grp_num;i++){
for(int j=m;j>0;j--){
if(j<obj[group[i].main].v) continue;
dp[j]=max(dp[j],obj[group[i].main].v*obj[group[i].main].w+dp[j-obj[group[i].main].v]);
for(int l=0;l<=group[i].num;l++){
for(int k=l+1;k<=group[i].num;k++){
if(j<obj[group[i].off[k]].v+obj[group[i].main].v+obj[group[i].off[l]].v) continue;
dp[j]=max(dp[j],obj[group[i].main].w*obj[group[i].main].v+obj[group[i].off[k]].w*obj[group[i].off[k]].v+obj[group[i].off[l]].w*obj[group[i].off[l]].v+dp[j-(obj[group[i].main].v+obj[group[i].off[k]].v+obj[group[i].off[l]].v)]);
}
}
}
}
}
void output(){
cout<<dp[m];
}
int main(){
input();
init();
dpfun();
output();
return 0;
}
审题以后发现,这道题和普通的背包问题就两个区别:一个是主附件机制的增加,另一个是求值由w之和变为了w*v之和。
第二个问题容易解决,把转移方程里面式子改一改就好了,主要是第一个。
首先想到的思路是可以打标签,我们在dp的过程中需要知道哪个主件买过。但这个思路很快被放弃了,我想到一个新的方案。
我们可不可以把主件和它对应的附件看做一组
这样就意味着,我们并不是从买与不买,而是从不买,只买主件,买一个附件,买两个附件中选择。
事实上题目中那句:只选择0,1,2个附件就给了提示,这里必须限制,要不然枚举的量很大。
转移方程:太难写了,意会即可,每一组都把这组可以买的方案都比较一下就完事儿了。
代码方面有些判断写的很长,但是其实很多只用复制粘贴。
我个人的习惯(虽然题目的变量名变了),但我还是n代表物品数量,m代表容量。

浙公网安备 33010602011771号