[树形DP] 换根DP

换根DP

某些树形DP问题中, 我们要求的值是类似 "以当前节点为根节点得到的答案", 却没有给出固定的根节点, 若仍然按照常规的树形DP思路对每个点进行DP, 我们对每一个节点均进行一次 DFS , 最后的复杂度是 \(O\left(n^2\right)\)

如果我们先假设任意一个点为根进行 DP, 求出当前树形结构下以每个点为根的子树的 DP 结果, 然后再将它的结果与父节点的结果一并处理, 也可以得到以它为根节点的整棵树的 DP 结果, 而这么做, 我们只需要进行两次 DFS, 就可以得到答案, 而这么做, 最后的复杂度只有 \(O\left(n\right)\) , 明显优于前者, 我们称这种做法为换根DP.

例题:

CodeForces 219 D Choosing Capital for Treeland CodeForces 洛谷

首先它说每条边是有方向的, 但实际为了方便 DP, 我们可以建双向边, 然后在边上记录当前的边是不是与它自身的指向相反

对于每个节点, 我们可以考虑先求出以某点为根 ( 假设是 \(1\) ) 的 DP 值.

然后我们对每个点的值更新, 方程为:

\[f\left(son\right) = f\left(father\right) \pm 1 \]

父亲儿子的边为正向时, 答案 \(+1\), 反之 \(-1\).

**为什么用 "\(=\)" ? **

  • 根据第一次DP的树形结构, 当前儿子的答案已经被包含到父亲中了, 所以采用 "\(=\)" .

代码:

# include <iostream>
# include <cstdio>
# define MAXN 2000005

using namespace std;

struct edge{
    int v, next;
    bool inv; // inv = 0 说明这条边是 u 到 v, = 1 是 v 到 u
}e[MAXN<<1];
int hd[MAXN<<1], cntE;

void AddE(int u, int v, int w){
    e[++cntE].v = v, e[cntE].inv = w, e[cntE].next = hd[u], hd[u] = cntE;
}

int f[MAXN], ans; // ans 记录每一轮计算得到的最小值

void Pre(int now, int fa){
    for(int i = hd[now]; i; i = e[i].next){
        if(e[i].v == fa) continue;
        f[now] += e[i].inv;

        Pre(e[i].v, now);
        
        f[now] += f[e[i].v];
    }
} // 先跑一遍以 i 为根的子树的答案

void DFS(int now, int fa){
    ans = min(f[now], ans);
    for(int i = hd[now]; i; i = e[i].next){
        if(e[i].v == fa) continue;
        f[e[i].v] = f[now] + (e[i].inv ? -1 : 1);
        DFS(e[i].v, now);
    }
} // 再跑一遍以 i 为根的全树的答案

int main(){
    int n, u, v;

    while(scanf("%d", &n) != EOF){
        cntE = 0;
        ans = 2e5+1;
        for(int i = 1; i < n; i++){
            scanf("%d%d", &u, &v);
            AddE(u, v, 0); AddE(v, u, 1);
            f[i] = 0;  
        }
        f[n] = 0;

        Pre(1, 0);
        DFS(1, 0);

        printf("%d\n", ans);
        for(int i = 1; i <= n; i++){
            if(f[i] == ans)
                printf("%d ", i);
        }
        printf("\n");
    }

    return 0;
}

posted @ 2020-07-15 10:57  ChPu437  阅读(132)  评论(2编辑  收藏  举报