zoukankan      html  css  js  c++  java
  • 【题解】IOI2005River 河流

      一节语文课想出来的玩意儿,调了几个小时……可见细心&好的代码习惯是有多么的重要 (;へ:)

      不过,大概竞赛最令人开心的就是能够一点一点的感受到自己的进步吧,一天比一天能够自己想出更多的题,A题之后刹那的欣喜……这些也正是坚持的最大动力。

      ……矫情了一波还是回到正题上吧:好像这题的正解是多叉树转二叉树?可是我并不会啊。首先我们看到这题的范围非常的小,所以肯定除了爆搜之外的乱搞都OK。(说不定爆搜+超强剪枝啥的也可以?但我没有尝试了……)那我们开始分析一波:动态规划最忌把所有东西都糊在一坨,重在分别状态,所以自然我们第一步需要建立出状态&分析出每一个节点独立的贡献。建立状态我们先采用最自然的想法:dp[u][k]表示u节点下k个节点建立了伐木场的最大贡献。

      最大贡献……什么是贡献?正向的统计节点的花费好像不是很容易,不如考虑减少的花费。如果我们在一个节点上建立了伐木场,则该节点向下往叶子上走,遇到的第一个伐木场之间的节点都会运向该伐木场,所节省的成本即为dis[u] * w[v]。那么我们的思路就形成了:dp[u][k] 表示u节点下k个节点建立了伐木场的最大贡献,且强制第k个建立在u节点上。对于每一个u节点而言,我们都遍历一遍它的子树,求出 f 数组,f[u][k] 表示u & u的子树中建立k个伐木场的最大贡献(不强制建在u节点上)。

      转移方程:背包求出组合最优解,求f数组时枚举:该节点建伐木场:伐木场数量+1,贡献 + dis[v] * w[v];该节点不建立伐木场:受当前 dp 建立的伐木场的‘管辖’,贡献 + dis[u] * w[v];最关键的地方还是在于将子树状态与父亲状态分离开来,这样就方便进行状态的转移了。

      虽然感觉复杂度非常的高:达到了(n^2)(单单考虑dfs)的级别,然而在luogu上rank1?大概是玄学……

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 105
    int n, K, cnp = 1, ans, head[maxn], w[maxn];
    int dis[maxn], size[maxn];
    int dp[maxn][maxn], f[maxn][maxn];

    struct edge
    {
        int to, co, last;
    }E[maxn];

    int read()
    {
        int x = 0, k = 1;
        char c;
        c = getchar();
        while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * k;
    }

    void add(int u, int v, int w)
    {
        E[cnp].to = v, E[cnp].co = w;
        E[cnp].last = head[u], head[u] = cnp ++;
    }

    void gmax(int &a, int b) { a = a > b ? a : b; }

    void DP(int u, int gra)
    {
        f[u][0] = dis[gra] * w[u];
        for(int i = head[u]; i; i = E[i].last)
        {
            DP(E[i].to, gra);
            for(int j = min(size[u], K); ~j; j --)
            {
                int lim = min(size[E[i].to], j);
                for(int k = 0; k <= lim; k ++)
                    gmax(f[u][j], f[E[i].to][k] + f[u][j - k]);
            }
        }
        for(int j = size[u]; j; j --) gmax(f[u][j], dp[u][j]);
    }

    void dfs(int u)
    {
        size[u] = 1;
        for(int i = head[u]; i; i = E[i].last)
        {
            int v = E[i].to;
            dis[v] = E[i].co + dis[u];
            dfs(v); size[u] += size[v];
        }
        dp[u][0] = 0, ans += dis[u] * w[u];
        memset(f, 0, sizeof(f));
        for(int i = head[u]; i; i = E[i].last)
        {
            DP(E[i].to, u);
            for(int j = min(K, size[u]); ~j; j --)
                for(int k = 0; k <= min(j, size[E[i].to]); k ++)
                    gmax(dp[u][j], f[E[i].to][k] + dp[u][j - k]);
        }
        for(int i = min(size[u], K); i; i --) dp[u][i] = dp[u][i - 1] + dis[u] * w[u];
    }

    int main()
    {
        n = read(), K = read();
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i ++)
        {
            w[i] = read(); int v = read(), z = read();
            add(v, i, z);
        }
        dfs(0); int tem = 0;
        memset(f, 0, sizeof(f));
        for(int i = head[0]; i; i = E[i].last)
        {
            DP(E[i].to, 0);
            for(int j = min(size[0], K); ~j; j --)
            {
                int lim = min(size[E[i].to], j);
                for(int k = 0; k <= lim; k ++)
                    gmax(f[0][j], f[E[i].to][k] + f[0][j - k]);
            }
        }
        for(int i = 1; i <= size[0]; i ++) tem = max(tem, f[0][i]);
        printf("%d ", ans - tem);
        return 0;
    }
  • 相关阅读:
    Oracle11g字典表
    python操作excel
    Linux基础
    Mysql的DML基本操作
    Mysq基本操作--DDL
    上传验证码
    验证码
    添加图像
    用cmd 添加 修改 删除数据库
    cmd命令提示符命令
  • 原文地址:https://www.cnblogs.com/twilight-sx/p/9053516.html
Copyright © 2011-2022 走看看