zoukankan      html  css  js  c++  java
  • [树形DP] 换根DP

    换根DP

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

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

    例题:

    CodeForces 219 D Choosing Capital for Treeland CodeForces 洛谷

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

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

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

    [fleft(son ight) = fleft(father ight) 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
    ", ans);
            for(int i = 1; i <= n; i++){
                if(f[i] == ans)
                    printf("%d ", i);
            }
            printf("
    ");
        }
    
        return 0;
    }
    

  • 相关阅读:
    Demo
    Demo
    z-yelir-~
    CSP考前总结
    NOIP刷题
    清北学堂
    qsing
    【csp模拟赛九】--dfs3
    【csp模拟赛九】--dfs2
    【csp模拟赛九】--dfs
  • 原文地址:https://www.cnblogs.com/Foggy-Forest/p/13304304.html
Copyright © 2011-2022 走看看