树上依赖背包总结
做了很多关于树上dp的题有些使用了nm^2的算法来进行dp,可是当m过大时方法不大试用,又可以对这种算法进行优化,如dfs序或者直接树上合并背包。当输出方案时又需要多叉转二叉进行dp寻找方案,很多方法于是乎总结一下。
w[i]表示第i个节点的重量,v[i]表示第i个节点的价值,f[i][j]表示以i为节点用了j的容量选出的最大值
1.直接树上进行dp,强制选择父亲然后枚举全部能选的容量再枚举子树能选的容量进行转移。向上更新。
void dfs(int x) { for(int i=w[x];i<=m;i++)f[x][i]=v[x]; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i];dfs(tn); for(int j=m-w[x];j>=0;j--)//01背包拿取for(int k=0;k<=j;k++) f[x][j+w[x]]=max(f[x][j+w[x]],f[x][j+w[x]-k]+f[tn][k]); } }
2.相对于第一个方法dp,本人感觉第二种优化的方法比较难理解。向下更新。
void dfs(int x)//做这个操作需要设一个超级源点 { for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; for(int j=0;j<=m;j++) f[tn][j]=f[x][j];//将父亲节点的信息传给子节点然后进行dp dfs(tn);//从下而上的dp for(int j=m;j>=w[tn];j--) f[x][j]=max(f[x][j],f[tn][j-w[tn]]+v[tn]);//神奇dp,仔细体会 } }//复杂度nm
3.多叉树转二叉树,左儿子右兄弟。
void dfs(int i,int j)//i为当前根节点,j为容积 { if(f[i][j]>0||i==0||j==0)return; dfs(rx[i],j);//不选。 f[i][j]=max(f[i][j],f[rx[i]][j]); int vx=j-w[i]; for(int k=0;k<=vx;k++) { dfs(ls[i],vx-k); dfs(rx[i],k); f[i][j]=max(f[i][j],f[rx[i]][k]+f[ls[i]][vx-k]+v[i]); } }//复杂度应该为nm
多叉树转化是:rx[x]=ls[i];ls[i]=x;
4.dfs序优化。
暂时还不会,主要应用为第2种方法!