动态规划总结

树形DP

一、在u的子树中,选择一个大小恰好为m的包含u点的连通块,最大的权值和(u,m<=2e3)

这样写虽然是三重循环但是时间复杂度是O(nm)的  

 

void dfs(int u) {
    sz[u] = 0;
    for (int v : son[u]) {
        dfs(v);
        
        for (int i = 0; i <= sz[u] + sz[v]; i ++) temp[i] = -INF;
        for (int i = 0; i <= sz[u]; i ++)
            for (int j = 0; j <= sz[v]; j ++)
                temp[i + j] = max(temp[i + j], dp[u][i] + dp[v][j]);
        
        for (int i = 0; i <= sz[u] + sz[v]; i ++) dp[u][i] = temp[i];
        sz[u] += sz[v];
    }
    sz[u] ++;
    for (int i = sz[u]; i; i --) dp[u][i] = dp[u][i - 1] + w[u];
}
View Code

 

二、题意跟上题一样但是n<=50000,m<=100

这样的话其实时间复杂度是O(nm)的

sz[u] = 0;
    for (int v : son[u]) {
        dfs(v);
        
        for (int i = 0; i <= min(sz[u] + sz[v], M); i ++) temp[i] = -INF;
        for (int i = 0; i <= min(sz[u], M); i ++)
            for (int j = 0; j <= sz[v] && i + j <= M; j ++)
                temp[i + j] = max(temp[i + j], dp[u][i] + dp[v][j]);
        
        for (int i = 0; i <= min(sz[u] + sz[v], M); i ++) dp[u][i] = temp[i];
        sz[u] += sz[v];
    }
    sz[u] ++;
    for (int i = min(sz[u], M); i; i --) dp[u][i] = dp[u][i - 1] + w[u];
View Code

三、每个点都有权值和重量,求一个重量恰好为k的包含根的连通块,最大的权值和(n<=1e3,m<=1e4),不存在输出0

特殊技巧:按dfs序做,r(x)表示序列中跳过x的子树的下一个位置,f[i][j]表示dfs序中 [i, n] 这一段的节点重量等于j的最大权值

f[i][j] = max(f[r(i)][j](不选), f[i + 1][j - vi] + wi),若不选这个节点则子树都跳过

 

void dfs(int u) {//dfs序
    l[u] = cnt ++;
    id[cnt] = u;
    for (auto v : son[u]) dfs(v);
    r[u] = cnt;
}

void solve() {
    for (int i = 1; i <= m; i ++) dp[n + 1][i] = -INF;
    for (int i = n; i; i --) {
        int u = id[i];
        for (int j = 0; j <= m; j ++) {
            dp[i][j] = dp[r[u] + 1][j];
            if (j >= v[u]) dp[i][j] = max(dp[i][j], dp[i + 1][j - v[u]] + w[u]);
        }
    }
}
View Code

 

posted @ 2022-11-16 17:04  Leocsse  阅读(38)  评论(0)    收藏  举报