首页 > 代码库 > [补档计划] 动态规划3 - 树形DP

[补档计划] 动态规划3 - 树形DP

  树形DP 是一类 OI 中的问题, 即对一棵树进行 DP .

  很多时候要进行两次 DP , 第一次处理子树内的情况, 第二次处理所有的情况.

  关键靠做题.

 

[CF697D] Puzzles

题意

  给定一棵 $N$ 个节点的树.

  求进行 DFS , 每个节点的时间戳的期望值.

分析 树的遍历 & 特性分析

  进行 DFS , 根的时间戳一定是 1 .

  $f[1] = 1$ .

  $f[next] = f[x] + \sum_{i = 1} ^ c \frac{1}{c} \sum_{son \ne next} size[son] + 1 = f[x] + \frac{1}{2}\sum_{son\ne next}size[son] + 1$ .

实现

技术分享
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>

#define F(i, a, b) for (register int i = (a); i <= (b); i++)
#define fore(k, x) for (register int k = hd[x]; k > 0; k = nx[k])

#define db double

const int N = 100005;

int n, tot, hd[N], nx[N], v[N];
int s[N];
db f[N];

inline int rd(void) {
    int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == -) f = -1;
    int x = 0; for (; isdigit(c); c = getchar()) x = x * 10 + c - 0; return x * f;
}

inline void Ins(int x, int y) { nx[++tot] = hd[x], hd[x] = tot, v[tot] = y; }
void Siz(int x) {
    s[x] = 1;
    fore(k, x) {
        Siz(v[k]);
        s[x] += s[v[k]];
    }
}

void Solve(int x) {
    long long sum = 0; fore(k, x) sum += s[v[k]];
    fore(k, x) {
        f[v[k]] = f[x] + 1 + (sum - s[v[k]]) / 2.0;
        Solve(v[k]);
    }
}

int main(void) {
    #ifndef ONLINE_JUDGE
        freopen("CF697D.in", "r", stdin);
        freopen("CF697D.out", "w", stdout);
    #endif

    n = rd(); F(i, 2, n) Ins(rd(), i);

    Siz(1);

    f[1] = 1.0;
    Solve(1);
    F(i, 1, n) printf("%0.8lf ", f[i]);
    printf("\n");

    return 0;
}
View Code

 

[补档计划] 动态规划3 - 树形DP