zoukankan      html  css  js  c++  java
  • ICPC2020南京站 M Monster Hunter

    传送门


    前几天的训练赛开了去年icpc的南京站,对这题印象挺深的。我们虽然推出了树上背包的转移方程,但是实现的时候因为优化没到位一直超时……没想到我以前写的所谓的(O(n^2))的树形背包竟然是假的。


    首先感觉自己dp这方面还是要加强,设了正确的状态后却迟迟没有推出转移方程,最后还是队友推出来的。

    (dp[u][0/1][j])表示以(u)为根的子树中,(u)这个点没有/有用咒语,且整棵子树内共用了(j)次咒语的情况下,杀掉所有怪的最小化费。

    而转移的时候考虑的是从(u)的所有孩子转移,即给孩子们(v)分配咒语次数,于是有:

    [egin{align} dp[u][0][j] &= min{sumlimits_{sum k_i = j}dp[v][0/1][k] + a[v] * [flg_v = 0] } + a[u], \ dp[u][1][j] &= min{sum_{sum k_i = j - 1} dp[v][0/1][k]} end{align} ]

    如果把每个孩子(v)的dp值看成物品,那就是一个分组背包:对于每个孩子,只能从(dp[v][0/1][k])中选一个,而每个孩子又必须选一个。那么就变成了比较显然的树形背包了。这题稍有一些区别的地方在于,每组物品必须选一个,因此在枚举(k)之前向(u)中放任意一个,再进行dp.

    关于树形背包,一定要把所有优化都加上,才能达到(O(n^2))。比如枚举(k)的时候要考虑上下界,如果没有下界,就会被链的数据卡掉,而如果没有上界,就会被菊花的数据卡掉。训练赛的时候就是因为没有下界优化而超时的。

    #include <bits/stdc++.h>
    using namespace std;
    #define enter puts("")
    #define space putchar(' ')
    #define In inline
    #define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
    #define Mem(a, x) memset(a, x, sizeof(a))
    typedef long long ll; 
    const int maxn = 2e3 + 5;
    const ll INF = 0x3f3f3f3f3f3f3f3f;
    In ll read()
    {
        ll ans = 0;
        char ch = getchar(), las = ' ';
        while(!isdigit(ch)) las = ch, ch = getchar();
        while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
        if(las == '-') ans = -ans;
        return ans;
    }
    In void write(ll x)
    {
        if(x < 0) putchar('-'), x = -x;
        if(x >= 10) write(x / 10);
        putchar(x % 10 + '0');
    }
    
    int n, a[maxn];
    struct Edge
    {
        int nxt, to;
    }e[maxn];
    int head[maxn], ecnt = -1;
    In void addEdge(int x, int y)
    {
        e[++ecnt] = (Edge){head[x], y};
        head[x] = ecnt;
    }
    
    int siz[maxn];
    ll dp[maxn][2][maxn];
    In void dfs(int now)
    {
        siz[now] = 1;
        for(int i = 0; i <= n; ++i) dp[now][0][i] = dp[now][1][i] = INF;
        dp[now][0][0] = a[now]; dp[now][1][1] = 0;
        forE(i, now, v)
        {
            dfs(v);
            for(int j = siz[now] + siz[v]; j >= 0; --j)
            {
            	if(j) dp[now][1][j] += dp[v][0][0]; 
                dp[now][0][j] += dp[v][0][0] + a[v];
                for(int k = max(0, j - siz[now]); k <= min(j, siz[v]); ++k)
                {
                    dp[now][0][j] = min(dp[now][0][j], dp[now][0][j - k] + min(dp[v][1][k], dp[v][0][k] + a[v])),
                    if(j) dp[now][1][j] = min(dp[now][1][j], dp[now][1][j - k] + min(dp[v][1][k], dp[v][0][k]));
                }
            }
    		siz[now] += siz[v];
        }
    }
    
    int main()
    {
        int T = read();
        while(T--)
        {
            Mem(head, -1), ecnt = -1;
            n = read();
            for(int i = 2; i <= n; ++i) addEdge(read(), i);
            for(int i = 1; i <= n; ++i) a[i] = read();
            dfs(1);
            for(int i = 0; i <= n; ++i)
            {
                write(min(dp[1][0][i], dp[1][1][i]));
                i == n ? enter : space;
            }
        }
        return 0;
    }
    
  • 相关阅读:
    c#中开发ActiveX的学习笔记
    [转]李战大师悟透delphi 第七章 组织你的模块
    在网页中实现QQ的屏幕截图功能
    vs.net的调试小技巧之#define debug(适合新手)
    socket中的byte消息格式设计
    [转]李战大师悟透delphi第五章 包
    [转]李战大师悟透delphi 第九章 多层体系结构
    [转]李战大师悟透delphi第一章 delphi的原子世界
    重温delphi之控制台程序:Hello World!
    silverlight中的socket编程注意事项
  • 原文地址:https://www.cnblogs.com/mrclr/p/15440435.html
Copyright © 2011-2022 走看看