子树合并背包类型的 dp 的复杂度证明

首先,我们发现,转移一颗子树的背包,实际上就是把该子树的根节点的所有儿子的子树背包合并,再与根节点合并。具体的,合并两颗子树的转移方程式如下:

f(u,i)=maxj+k=i{f(v1,j)+f(v2,k)}

于是有如下伪代码:

dfs(u) : su = 1 f(u, 1) = w[u] for (v in u) sv = size(v) for (i : su ~ 1) for (j : 1 ~ sv) f(u, i + j) = max(f(u, i + j), f(u, i) + f(v, j)) su = su + sv return

乍一看合并两颗子树大小分别为 x,y 的子树的时间复杂度为 O(xy),所以总时间复杂度应为 O(n3)。但是我们考虑到对于每一对结点,它被统计的次数只有一次,而这次统计是在转移它们 lca 时被统计的,所以可以得到时间复杂度 O(n2)

但是,这并不是最终的时间复杂度,它还可以得到一个更优的上界:O(nk),其中 k 为背包值域。具体推导过程如下:
1. 如果一颗子树的 size>k,那么对于大于 k 的那部分背包是不需要的,即将其背包大小限制为 k
2. 如果一颗子树的 sizek,不变。
3. 如果转移完一颗子树,它的 sizek,那么转移完整颗子树的时间复杂度为 O(size2)O(size×k),而对于若干个 sizek 的不相交子树,它们的 size 之和一定 n,所以这一部分的时间复杂度为 O(nk)
4. 如果转移完一颗子树,它的 size>k,那么它的儿子有一些是 sizek,有一些是 size>k。对于不相交的 size>k 的子树,它的个数 nk,而合并一对的时间复杂度为 O(k2),所以这一部分的时间复杂度为 O(nk);对于 sizek 的情况,如果目前合并到的 size>k,那么将其与剩下的 sizek 的儿子合并,时间复杂度为 O(size×k),而 sizen,所以这一部分的时间复杂度为 O(nk);如果要合并出 size>k,需要将已经合并出的最大的 sizek 与一个新儿子合并,时间复杂度 O(k2),而这样的情况数 nk,所以这一部分的时间复杂度为 O(nk)
于是,我们推出了总的时间复杂度 O(nk)


__EOF__

本文作者TrueFalse
本文链接https://www.cnblogs.com/cqbzljh/p/17743671.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   cqbzljh  阅读(68)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
点击右上角即可分享
微信分享提示