背包九讲学习
DD大牛的背包九讲真是神。等我全看懂了回来更博。
upd
如果不是我看错了,那一定是神犇讲的太明白了。
我尝试用几句话概括一下前面部分(P01-P05),每回重新看有些麻烦。
$cost\rightarrow花费,value\rightarrow价值$
01背包
决策:选或不选,$f[v]$表示v花费可以得到最大价值。则
$for\space v=V...cost$
$f[v]=max(f[v],f[v-cost]+value)$
只能由上一层转移来,所以倒序保证不影响。
想要恰好$V$就只有$v=0$合法即初始化$f[0]=0$,其他为$-inf$。不需要都赋为$0$。
所有问题的基础。$O(nV)$
完全背包
总是记不住这些名字,就当做是“完全随便拿”的背包吧,,,则
$for\space v=cost...V$
$f[v]=max ( f[v],f[v-cost]+value ) $
类似01,可以同层转移,就这样咯。$O(nV)$
多重背包
类似完全背包。加一些限制。是“可以重复多拿”的,但不能太多,每个物品有限制。
想法是拆成二进制的。比如$13=1+2+4+6$这么拆,不要记错了。
楼教主“男人八题”那个单调队列的暂时不会,挖坑。
废话
一些显而易见的贪心。超过$V$的不能选,完全背包里$cost$又大$value$又小的,不去选ta。
混合背包
好像没啥说的,前三个混到一块各做各的该咋做咋做就行。。
二维花费
那就二维状态,类比一下就行。则
$for\space v=V...cost1$
$for\space u=U...cost2$
$f[v][u]=max(f[v][u],f[v-cost1][u-cost2]+value)$
只要不从同一层状态转移过来就好。
写完感觉好傻啊。
所以说重点在于泛化物品咋做。
泛化物品定义成这样:对于你赋给它的每一个花费会获得对应的价值。
更数学化一些,这是一个函数,定义域为$V$,自变量是花费$cost$,函数值是价值$value$。对于每个$cost$都有一个对应的$value$,如果某个$cost$没有定义,我们可以视之为$-inf$。
具体来说,比如01背包中的背包,当且仅当赋给它$cost$值时有函数值$value$,其他为$-inf$。
又如多重背包,我们甚至可以写出解析式:$f[x]=x\div cost \times value (cost|x)$
当然啦,我们抽象出这样一个定义又有什么用呢?
泛化物品可以做加法!!!
对于两个泛化物品$a,b$,我们把他们加成一个泛化物品$c$,即
$c[v]=max(a[i]+b[v-i])(0\leq i\leq v)$
复杂度$O(V^2)$。
于是现在再来说刚才没说的分组背包和有依赖的背包。
分组背包
物品分成了$k$组,每组内物品只能选一个或不选。
每组做一次01背包,就可以当做$k$个泛化物品做了。$O(kV^2)$
(树形)依赖背包
依赖显然不会存在环,所以普通的依赖也可以转成树形。
然后通法就是:儿子当做泛化物品,则父亲是儿子之和,递归处理之。
这里会补上一个代码。
至此我对背包九讲的理解结束了!!现在看起来真的不是很难懂的东西但却十分巧妙,尤其是用函数的思想将物品转化成泛化物品,让我不得不佩服DD大牛%%%。最后一讲我没有细看,如果有时间我应该会回来更博写一下。
upd
树形背包有$O(NM)$做法的啊。所以上面的代码就不补了(已经用那种做法水掉CTSC1998选课了,时间上差了好多顿时不想贴那个的代码了)。
$dp[i][j]$表示选择了节点 $i$ (及其所有祖先节点) (即根节点到 $i$ 的路径——记作 $Pi$ 上的所有节点),此外再在 $Pi$ 的左侧和子树 $i$ 中选择总体积不超过 $j$ 的物品的最大价值。
void dfs(int u,int f,int cost) { if(cost<=0)return ; for(int i=0;i<e[u].size();i++){ int v=e[u][i]; if(v==f)continue; for(int j=0;j<=cost;j++)dp[v][j]=dp[u][j]; dfs(v,u,cost-b[v]); for(int j=b[v];j<=cost;j++)dp[u][j]=max(dp[u][j],dp[v][j-b[v]]+a[v]); } }