Loading

关于树上背包的 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)\) 能过,才去了解了其复杂度。

posted @ 2022-10-03 21:50  purplevine  阅读(85)  评论(0编辑  收藏  举报