张奕涵 @ 2017-08-09 20:13:06
看了九讲的泛化物品之后想出的一个做法,不能说完全理解,但还是有这样的一点个人看法吧。
1,按照物品组处理为多个泛化物品
既然有主件和附件之分,选附件必须要选主件,那么也就是说,可以把整个物品组看成一个泛化物品,当分配给它的价值小于主件所需价值时,反馈价值为0。当超过主件价值时,反馈价值至少为主件的反馈价值。而在分配价值逐渐上升的时候,会发现达到主件加上某个附件的价值,继续上升就出现了矛盾,到底选择哪个附件才是最优方案?只需取当前价值下的max即可。分配价值不断上升而不因前面被置成不同物品而不同。
【举个栗子】
有这样一个物品组:(v,p)
主件:200 4 附件1:100 3 附件2:150 3
设f[i]表示分配给这个物品组i价值时能得到的最大反馈。
那么显然,f[0..199]=0,f[200..299]=800,f[300..349]=1100,f[350..449]=1250,f[450..n]=1550。
2,计算所有泛化物品的和
弱弱的说一句具体我也并不很懂
由于两个泛化物品的和为:“如果给定了两个泛化物品 h 和 l ,要用一定的费用从这两个泛化物品中得到最大的价值,这个问题怎么求呢?事实上,对于一个给定的费用 v ,只需枚举将这个费用如何分配给两个泛化物品就可以了。同样的,对于 0...V 中的每一个整数 v ,可以求得费用 v 分配到 h 和 l 中的最大价值 f(v) 。也即
f(v) = max {h(k) + l(v − k) | 0 ≤ k ≤ v}”(引自背包九讲O(v2),这里讲的很清楚,我自己写肯定讲不清楚orz)
那么只需要不断地累加这些泛化物品(可以用一个ans,从1到物品组数去加,最后输出),就可以求出这个问题最后的答案。这样的话,面对附件更多的问题就可以有这样一个通用的解法,而不用去枚举方案。
以上皆为个人观点,欢迎大佬们批评指正!如果有什么讲的不明白的地方也欢迎留言交流!
by panda_2134 @ 2017-08-10 11:14:26
我记得背包九讲上面说了这个题目的一般情况的。。。
实际上这里泛化物品的处理没必要暴力求解泛化物品,01+分组背包可以解决。
对于一个主件-附件组,假设主件的价值为
然后把体积为
本人蒟蒻,说错了欢迎吐槽orz
by panda_2134 @ 2017-08-12 19:41:37
这里是本蒟蒻的50pts代码,泛化背包写的,不知道哪里错了qwq
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
const int MAXN = 60, MAXV = 32000, INF = 0x3f3f3f3f;
int V,N,w[MAXN+10],c[MAXN+10],dep[MAXN+10][10],genItem[MAXV+10],opt[MAXV+10];
int main(){
scanf("%d%d",&V,&N);
V/=10;
for(int i=1; i<=N; i++) {
static int v,p,q;
scanf("%d%d%d",&v,&p,&q);
c[i]=v/10;w[i]=v*p;
dep[q][++dep[q][0]]=i;
}
for(int i=1; i<=dep[0][0]; i++) {
static int mainItem,subItem;
mainItem=dep[0][i];
for(int j=V; j>=0; j--)
if(j>=c[mainItem])
genItem[j]=w[mainItem];
else
genItem[j]=-INF;
for(int j=1; j<=dep[mainItem][0]; j++) {
subItem=dep[mainItem][j];
for(int k=V; k>=c[mainItem]+c[subItem]; k--)
genItem[k]=max(genItem[k],genItem[k-c[subItem]]+w[subItem]);
}
for(int j=V; j>=c[mainItem]; j--)
for(int k=c[mainItem]; k<=j; k++)
opt[j]=max(opt[j],opt[j-k]+genItem[k]);
}
printf("%d\n",opt[V]);
}
by joyemang33 @ 2017-08-12 20:15:43
您想说的是不是,对于附件没有从属主件的情况,可以把一个主件以及它的所有附件看成一组,然后对于每一组做一次背包,求出给这一组分配某一体积的最大价值,然后对于再对所有组做一次背包算出总贡献
实际上对于附件依赖主件件的情况,可以把之前说到的分成一组更具体的构成一棵树,表示子节点依赖父亲,对每一颗树进行一次树形DP,F[i][j]表示对第i个节点以及其子树分配 j 空间的贡献,对于每一组分配j的贡献实际上就是改组的树的根节点u的F[u][j],然后再做一次正常的DP就可以求出解了,不是很明白和泛化物品有什么太大的关系Orz
by panda_2134 @ 2017-08-21 10:57:34
@mangoyang 讲道理树上背包就是求解泛化物品啊
by panda_2134 @ 2017-08-21 12:03:25
@jn15张奕涵 好的我知道我上面那个泛化背包哪里错了
数组开小了。。。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
const int MAXN = 60, MAXV = 32000, INF = 0x3f3f3f3f;
int V,N,w[MAXN+10],c[MAXN+10],dep[MAXN+10][MAXN+10],genItem[MAXV+10],opt[MAXV+10];
int main(){
scanf("%d%d",&V,&N);
V/=10;
for(int i=1; i<=N; ++i) {
static int v,p,q;
scanf("%d%d%d",&v,&p,&q);
c[i]=v/10;w[i]=v*p;
dep[q][++dep[q][0]]=i;
}
for(register int i=1; i<=dep[0][0]; ++i) {
register int mainItem,subItem;
mainItem=dep[0][i];
for(register int j=V; j>=c[mainItem]; j--)
genItem[j]=w[mainItem];
for(register int j=c[mainItem]-1; j>=0; j--)
genItem[j]=-INF;
for(register int j=1; j<=dep[mainItem][0]; ++j) {
subItem=dep[mainItem][j];
for(register int k=V; k>=c[mainItem]+c[subItem]; --k)
genItem[k]=max(genItem[k],genItem[k-c[subItem]]+w[subItem]);
}
for(register int j=V; j>=c[mainItem]; j--)
for(register int k=c[mainItem]; k<=j; ++k)
opt[j]=max(opt[j],opt[j-k]+genItem[k]);
}
printf("%d\n",opt[V]);
}
by panda_2134 @ 2017-08-21 12:03:48
以及泛化背包最后一组数据有点卡常数?
by 张奕涵 @ 2017-08-23 12:31:47
大佬。。。大佬。。。