题目描述
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NN元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 附件
电脑 打印机,扫描仪
书柜 图书
书桌 台灯,文具
工作椅 无
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有00个、11个或22个附件。附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的NN元。于是,他把每件物品规定了一个重要度,分为55等:用整数1-51−5表示,第55等最重要。他还从因特网上查到了每件物品的价格(都是1010元的整数倍)。他希望在不超过NN元(可以等于NN元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第jj件物品的价格为v_[j]v[j],重要度为w_[j]w[j],共选中了kk件物品,编号依次为j_1,j_2,…,j_kj1,j2,…,jk,则所求的总和为:
v_[j_1] imes w_[j_1]+v_[j_2] imes w_[j_2]+ …+v_[j_k] imes w_[j_k]v[j1]×w[j1]+v[j2]×w[j2]+…+v[jk]×w[jk]。
请你帮助金明设计一个满足要求的购物单。
输入输出格式
输入格式:
第11行,为两个正整数,用一个空格隔开:
N mNm (其中N(<32000)N(<32000)表示总钱数,m(<60)m(<60)为希望购买物品的个数。) 从第22行到第m+1m+1行,第jj行给出了编号为j-1j−1的物品的基本数据,每行有33个非负整数
v p qvpq (其中vv表示该物品的价格(v<10000v<10000),p表示该物品的重要度(1-51−5),qq表示该物品是主件还是附件。如果q=0q=0,表示该物品为主件,如果q>0q>0,表示该物品为附件,qq是所属主件的编号)
输出格式:
一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<200000<200000)。
有附属关系的背包问题
还好这题最多两个附属品 那么就是五种状态转移
不选 只选主件 主件加附件1 主件加附件2 主件加所有附件
注意要大于体积才能转移!!因为这个wa了
#include<bits/stdc++.h> using namespace std; //input #define rep(i,x,y) for(int i=(x);i<=(y);++i) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m); #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s) #define LL long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) ////////////////////////////////// #define N 65 #define inf -0x3f3f3f3f int zhuv[N]; int zhuc[N]; int fuv[N][3]; int fuc[N][3]; int dp[32005]; int main() { int m,n; RII(m,n); rep(i,1,n) { int a,b,c; RIII(a,b,c); if(!c) { zhuv[i]=a; zhuc[i]=a*b; } else { fuv[c][0]++; fuv[c][fuv[c][0]]=a; fuc[c][fuv[c][0]]=a*b; } } rep(i,1,n) for(int j=m;zhuv[i]&&j>=zhuv[i];--j) { dp[j]=max(dp[j],dp[j-zhuv[i] ]+zhuc[i] ); if(j>=zhuv[i]+fuv[i][1]) dp[j]=max(dp[j],dp[j-zhuv[i]-fuv[i][1] ]+zhuc[i]+fuc[i][1] ); if(j>=zhuv[i]+fuv[i][2]) dp[j]=max(dp[j],dp[j-zhuv[i]-fuv[i][2] ]+zhuc[i]+fuc[i][2] ); if(j>=zhuv[i]+fuv[i][1]+fuv[i][2]) dp[j]=max(dp[j],dp[j-zhuv[i]-fuv[i][1]-fuv[i][2] ]+zhuc[i]+fuc[i][1] +fuc[i][2] ); } cout<<dp[m]; }
但是很多情况下状态转移不止这几种
得使用背包九讲里面的普适方法!!!
可以先对每种主件的 附件的集合 进行一次 0101 背包处理,就可以先求出 对于每一种主件包括其附件的组合中,每种花费的最大价值(读不懂的同学可以看后面解释)。
对于每一种主件的01背包处理:
for i:主件k的所有附件
for j:价值(0 ~ n-主件价值)
01背包递推
这样可以得到主件 kk 的附件中费用依次为 0 sim n-v[k]0∼n−v[k] 时的相应最大价值 f[0 sim n-v[k]]f[0∼n−v[k]],那么我们就得到了主件 kk 及其附件集合的 n-v[k]+1n−v[k]+1 种不同选择情况,其中费用为 v[k]+tv[k]+t 的物品的价值就是 f[t]+v[k]*p[k]f[t]+v[k]∗p[k] 。
对于每一个主件处理出的情况,在 n-v[k]+1n−v[k]+1 种情况之中只能最多选择一种选入最终答案之中(把上面文字多读几遍吧),原问题便转化成一个分组背包问题。
如果你不知道分组背包的话:
for 所有的组k
for v = V ... 0
for 所有的i属于组k
f[v]=max{f[v],f[v-c[i]]+w[i]}
这题的分组背包部分应该是这样写的:
for 所有的主件数k
for j = n ... 0
for 所有的主件和附件的组合属于组k
f[j]=max{f[j],f[j-v[i]]+v[i]*p[i]}
#include<bits/stdc++.h> using namespace std; //input #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m); #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s); #define LL long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) ////////////////////////////////// #define N 100 struct { int v,c; int id; }a[N],fu[N][N];//fu为主件i的第j个附件 int num[N];//附件个数 int c[N][N];//第i组第j个物品的价值 int v[N][N]; int dp[32000]; int cnt[N];//第i组有几个状态 int main() { int n,m; RII(m,n); rep(i,1,n)//读入数据 储存到结构体里 { RIII(a[i].v,a[i].c,a[i].id); a[i].c*=a[i].v; if(a[i].id) { num[ a[i].id ]++; fu[ a[i].id ][ num[ a[i].id ] ].v=a[i].v; fu[ a[i].id ][ num[ a[i].id ] ].c=a[i].c; fu[ a[i].id ][ num[ a[i].id ] ].id=a[i].id; } } rep(i,1,n)//开始对组的处理 { if(num[i])//不是主件 { CLR(dp,-1);//恰好背包初始化 dp[0]=0;//恰好背包 rep(j,1,num[i]) for(int k=m-a[i].v;k>=fu[i][j].v;k--) if(dp[k-fu[i][j].v ]!=-1)//注意恰好背包 dp[k]=max(dp[k],dp[k-fu[i][j].v ]+fu[i][j].c); rep(j,0,m-a[i].v)//储存每组的所有情况 if(dp[j]!=-1)//恰好背包的判断 这种附件组合满足题意 { cnt[i]++; v[i][cnt[i] ]=j+a[i].v; c[i][cnt[i] ]=dp[j]+a[i].c; } } if(!a[i].id)//如果是主件 该组只有一个 { cnt[i]++; v[i][cnt[i] ]=a[i].v; c[i][cnt[i] ]=a[i].c; } } CLR(dp,0); rep(i,1,n) for(int j=m;j>=0;j--)//第几组 rep(k,1,cnt[i])//背包容量 if(j>=v[i][k])//该组第几个状态 dp[j]=max(dp[j],dp[j-v[i][k]]+c[i][k] ); int maxx=0; rep(i,0,m) maxx=max(maxx,dp[i]); cout<<maxx; return 0; }