关于树上背包的 O(n^2) 做法
众所周知,树上背包能做到 \(O(n^2)\)。现在来对它的复杂度做分析。
// O(n^2)
for(int k = siz[u] + siz[v]; k >= 0; k--){
dp[u][k] = inf;
for(int j = max(k - siz[u], 1); j <= min(siz[v], k); j++){
dp[u][k] = min(dp[u][k], dp[u][k - j] + dp[v][j]);
}
}
siz[u] += siz[v];
// O(n^3)
for(int k = siz[u] + siz[v]; k >= 0; k--){
dp[u][k] = inf;
for(int j = 1; j <= siz[v] && j <= k; j++){
dp[u][k] = min(dp[u][k], dp[u][k - j] + dp[v][j]);
}
}
siz[u] += siz[v];
分析下者复杂度上界是 \(O(n^3)\) 是显然的。上层循环、下层循环的复杂度上界是 \(O(n)\),加上 dfs 的 \(O(n)\),是 \(O(n^3)\)。
构造一条链即可卡到理论复杂度上界:对于深度为 \(m\) 的节点,其运算量为 \(O(m^2)\)。
究其原因,是每次把 \(v\) 中的信息完整合并到 \(u\) 时,总会有一些多余情况,或者说,无法直接合并的情况。比如,\(k-j \leq siz_u\) 时,\(dp_{u,k-j}\) 才已经经过计算。现在通过调整循环范围优化掉这里。
复杂度我也只能给个偏感性理解:
当把 \(v\) 合并到 \(u\) 上时,因为 \(u\) 中原来没有含 \(v\) 的信息,所以每次合并不会重复合并一对点的信息。又因为现在循环范围内每一次合并都合法,所以最多合并了 \(O(n^2)\) 对点的信息,所以复杂度是 \(O(n^2)\)。
大开眼界。我初二时自己想到了如此优化上下界,但是却无法证明其复杂度。直到今天,\(O(n^3)\) 的树上背包被卡,\(O(n^2)\) 能过,才去了解了其复杂度。
本文来自博客园,作者:purplevine,转载请注明原文链接:https://www.cnblogs.com/purplevine/p/16751390.html