[ICPC2020南京M] Monster Hunter

[ICPC2020南京M] Monster Hunter - 树形dp,背包dp

Description

给定一颗大小为 (n) 的有根树,每个结点都有点权 (hp[i]),选取点的代价是 (hp[i] + sum_{son of i} hp[j]),且其父亲也被选 现在可以在这棵树中删除 (0-n) 个点,问删除这些点的情况下最小的代价分别是多少

Solution

(f[p][v][0/1]) 表示 (p) 子树内,枪毙 (v) 个点,是否枪毙 (p) 时的总代价

转移枚举的时候注意顺序,否则会复杂度会退化到立方

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int N = 2005;

int n, a[N];
vector<int> g[N];
int siz[N], f[N][N][2], h[N][2];

void dfs(int p)
{
    siz[p] = 1;
    f[p][0][0] = a[p];
    f[p][1][1] = 0;
    int cnt = 1;
    random_shuffle(g[p].begin(), g[p].end());
    for (int q : g[p])
    {
        dfs(q);
        siz[p] += siz[q];
        for (int i = siz[p]; i >= 0; i--)
            h[i][0] = h[i][1] = 1e18;
        for (int t = cnt; t >= 0; t--)
        {
            for (int j = 0; j <= siz[q]; j++)
            {
                int i = t + j;
                h[i][0] = min(h[i][0], min(f[q][j][0] + a[q] + f[p][i - j][0], f[q][j][1] + f[p][i - j][0]));
                h[i][1] = min(h[i][1], min(f[q][j][0] + f[p][i - j][1], f[q][j][1] + f[p][i - j][1]));
            }
        }
        cnt += siz[q];
        for (int i = cnt; i >= 0; i--)
            f[p][i][0] = h[i][0], f[p][i][1] = h[i][1];
    }
}

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
        g[i].clear(), siz[i] = 0;
    for (int i = 2; i <= n; i++)
    {
        int p;
        cin >> p;
        g[p].push_back(i);
    }
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= n; j++)
            for (int k = 0; k < 2; k++)
                f[i][j][k] = 1e18;
    dfs(1);
    for (int i = 0; i <= n; i++)
        cout << min(f[1][i][0], f[1][i][1]) << " ";
    cout << endl;
}

signed main()
{
    ios::sync_with_stdio(false);

    int t;
    cin >> t;

    while (t--)
    {
        solve();
    }
}
原文地址:https://www.cnblogs.com/mollnn/p/14587084.html