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;
    }
    

  • 相关阅读:
    蛙蛙推荐:改进了一个DBAccess类,顺便说说啥是线程安全
    蛙蛙推荐:SQLServer优化资料整理
    快速配置Eclipse3.1.1+Tomcat5.0+Lomboz dodo
    建立asp.net应用程序提示:无法与服务器建立连接 dodo
    tomcat连接sqlserver配置 dodo
    如何让DNN页面中的html控件中的文本项显示为中文? dodo
    DropDownList 不能有多个项被选定 dodo
    转换DATAREADER为 dataset dodo
    使用ComponentArt.WebUI.for.Asp.net.3.0的TreeView控件 dodo
    ASP.NET的WEB项目不能添加WEB用户控件的解决 dodo
  • 原文地址:https://www.cnblogs.com/Foggy-Forest/p/13304304.html
Copyright © 2011-2022 走看看