zoukankan      html  css  js  c++  java
  • Luogu 3354 [IOI 2005] Riv

    题面

    这个题是一道树形DP,非常能开阔眼界。

    状态

    f[i][j][k] 表示 以i为子树,在j建一个伐木场,i得子树中一共允诺了k个伐木场。

    这里得允诺就是一个套路性得DP方法,我们发现依照一开始f[i][j]表示在i得子树中,点集为j没有伐木场这种树形DP好像不可以转移,我们就能考虑到,在转移的过程中有必要知道哪里有伐木场,才能进行转移。

    转移得时候依靠背包,因为当前点i无非只要建伐木场和不建伐木场,就能得到转移

    f数组表示i没建造了伐木场, g表示i建了造伐木场

    f[i][j][k] += f[son[i]][j][0];//i 得子树都可以直接用i曾经允诺过建造伐木场得伐木场

    g[i][j][k] += f[son[i]][i][0];

    然后进行普通的树形背包转移

    接着我们知道,当前得价值就是指定j有伐木场,价值还没有更新,所以需要更新价值,即

    其实我们可以看出来g[i][stac[j]][k] 是一个冗余得状态。

    他所多记录的信息就是i有没有伐木场,完全可以被f[i][i][k-1]这个状态替代。

    所以我们就可以少转移了。

    贴个代码

    #include <bits/stdc++.h>
    
    using namespace std;
    //转移:枚举字数里有多少个伐木场
    
    typedef long long LL;
    const int N = 120;
    const int M = 55;
    
    int n, K, dep[N];
    int val[N], head[N], cnt;
    int stac[N], top;//树上枚举一般都要用栈
    LL f[N][N][M];
    int in1, in2;
    
    struct Edge{
        int to;
        int nxt;
        int wei;    
    }e[N << 1];
    
    void add(int x,int y,int z){
        e[++cnt].to = y;
        e[cnt].wei = z;
        e[cnt].nxt = head[x];
        head[x] = cnt;
    }
    
    
    void dfs(int i){
        stac[++top] = i;
        for(int t = head[i];t;t = e[t].nxt){
            int v = e[t].to;
            dep[v] = dep[i] + e[t].wei;
            dfs(v);
            for(int j = 1;j <= top;++ j){
                for(int k = K;k >= 0;-- k){
                    f[i][stac[j]][k] += f[v][stac[j]][0];//子树都不用建造了
                    for(int x = 0;x <= k;++ x)
                    f[i][stac[j]][k] = min(f[i][stac[j]][k],
                     f[i][stac[j]][k-x] + f[v][stac[j]][x]);
                }
            }
        }
        for(int j = 1;j <= top;++ j){
            for(int k = 0;k <= K;++ k){
                if(k >= 1)
                f[i][stac[j]][k] = min(f[i][stac[j]][k] + val[i] * (dep[i] - dep[stac[j]]), f[i][i][k-1]);
                else f[i][stac[j]][k] += val[i] * (dep[i] - dep[stac[j]]);
            }
        }
        top --;
    }
    
    int main(){
        scanf("%d %d", &n, &K);
        for(int i = 1;i <= n;++ i){
            scanf("%d %d %d", &val[i], &in1, &in2);
            add(in1, i, in2);
        }
        dfs(0);
        printf("%d", f[0][0][K]);
        return 0;
    }
  • 相关阅读:
    SQLServer 备份和还原
    Ling To DataTable
    asp.net安全防范
    SQLServer 定时备份(一)
    asp.net网站安全常见问题与防范
    SQLServer 定时备份(二)
    flex大法:一网打尽所有常见布局
    BetterScroll源码阅读顺便学习TypeScript
    快速搭建一个代码在线编辑预览工具
    一个简单标注库的插件化开发实践
  • 原文地址:https://www.cnblogs.com/Shu-Kuang/p/12792636.html
Copyright © 2011-2022 走看看