感觉好久没更新博客了,不知道你们有没有想我(反正也没几个人看哈哈哈)~~~
最近接到上级指示开始写openjudge……
今天我终于回来了……
讲讲树形背包吧qwq。。。
f [ x ] [ y ]表示以 x 为父亲节点,选 y 个结点的最优解是多少,每一次按照常理更新即可,对于父子关系的存储,建议开vector(比较新奇就比较喜欢,有的时候可以代替前向星,虽然我没那么写过)。。。
我就解释一点,认为比较重要:
看这段:
for(int i=0;i<son[x].size();i++) { int y=son[x][i]; dp(y); for(int t=m;t>=0;t--) for(int j=t;j>=0;j--) f[x][t]=max(f[x][t],f[y][j]+f[x][t-j]); }
有人可能要问:不是刚刚说f [ s ] [ e ]表示以 s 为父亲节点,选 e 个结点的最优解是多少吗?那你 f [ y ] [ j ] 不是包括在 f [ x ] [ t - j ]里面嘛???
乍一听有理……
实际上……显然不对。。。
我们对于来自 y 对 x 的更新,一定是在从 y 节点回溯的时候第一次遇到 x (其实也就能遇到一次),那么这个时候 f [ x ] [ ... ] 里并不包含 f [ y ] [ ... ] 的任何信息(我们不就是因为这个才要更新的嘛……),所以我们就利用刚刚更新好的最优解 f [ y ] [ ... ] 来更新它的父亲节点 f [ x ] [ ... ] 。
还有个小细节:
就算这个点是叶子节点,也要执行下述操作:
for(int i=m;i>=1;i--) f[x][i]=f[x][i-1]+s[x];
如果有人问我边界问题,会不会一直son下去,建议你再看看我的代码,用了点小聪明(捂嘴笑)~~~
其实还有一点比较重要(emmm……):
因为可能不止一门课比较独立,这样就会出现森林,所以方便起见设一虚根0。
差不多啦,放代码(去吧——皮卡丘!)
#include<cstdio> #include<vector> #include<iostream> #include<cstring> using namespace std; vector<int> son[350]; int f[350][350],s[350],n,m; void dp(int x) { f[x][0]=0; for(int i=0;i<son[x].size();i++) { int y=son[x][i]; dp(y); for(int t=m;t>=0;t--) for(int j=t;j>=0;j--) f[x][t]=max(f[x][t],f[y][j]+f[x][t-j]); } if(x) for(int i=m;i>=1;i--) f[x][i]=f[x][i-1]+s[x]; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { int x; scanf("%d%d",&x,&s[i]); son[x].push_back(i); } memset(f,0xcf,sizeof(f)); dp(0); printf("%d",f[0][m]); return 0; }