树上背包优化 - 时间复杂度证明
树上背包优化 - 时间复杂度证明
树上背包
- 例题
- 树上背包顾名思义,就是在树上做背包 dp
- 树上背包的模板代码如下
void dfs(int x){
sz[x] = 1;
if(x >= n - m + 1){
dp[x][1] = -a[x];
return;
}
for(PII e : eg[x]){
int nxt = e.first;
dfs(nxt);
sz[x] += sz[nxt];
for(int j = m; j >= 0; j--){
for(int h = 1; h <= j; h++){
dp[x][j] = min(dp[x][j], dp[x][j - h] + e.second + dp[nxt][h]);
}
}
}
}
- 但是这样的循环显然是
的 - 这有一种固定的优化方法,可以将这三个循环优化到理论
优化
void dfs(int x){
if(x >= n - m + 1){
dp[x][1] = -a[x];
sz[x] = 1;
return;
}
for(PII e : eg[x]){
int nxt = e.first;
dfs(nxt);
for(int j = sz[x]; j >= 0; j--){ // 注意两个循环的范围!
for(int h = sz[nxt]; h >= 0; h--){
dp[x][j + h] = min(dp[x][j + h], dp[x][j] + e.second + dp[nxt][h]);
}
}
sz[x] += sz[nxt]; // 注意这里!
}
}
- 其实这个优化就是利用子树的背包中有效最大值和有效最小值来优化
- 证明如下:
时间复杂度证明
感性理解:
- 可以将
数组的第二维单独看 - 那么可以转化为一维 dp,显然时间复杂度是
,因为总共会发生 种转移 - 如果用
(大小)来约束枚举的状态,就可以保证没有枚举多余的转移 - 则第二维总共枚举了
种转移,于是 j 和 h 的循环总共时间复杂度是
理论证明
就那下面这一份代码来解析。
点击查看代码
void dfs(int x){
if(x >= n - m + 1){
dp[x][1] = -a[x];
sz[x] = 1;
return;
}
for(PII e : eg[x]){
int nxt = e.first;
dfs(nxt);
for(int j = sz[x]; j >= 0; j--){ // 注意两个循环的范围!
for(int h = sz[nxt]; h >= 0; h--){
dp[x][j + h] = min(dp[x][j + h], dp[x][j] + e.second + dp[nxt][h]);
}
}
sz[x] += sz[nxt]; // 注意这里!
}
}
本文作者:hhhqx
本文链接:https://www.cnblogs.com/huangqixuan/p/17652214.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步