icpc2020南京站 M.Monster Hunter (树形背包)
题目链接:https://ac.nowcoder.com/acm/problem/216013
设 \(dp[i][j][0/1]\) 表示杀到第 \(i\) 个节点,已经用魔法杀了 \(j\) 个怪物,当前节点使不使用魔法,所消耗的最小能量
注意下树形背包的写法
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 2010;
const ll inf = 1ll << 60ll;
int T, n, m;
int a[maxn];
ll tmp[maxn][2], dp[maxn][maxn][2];
int h[maxn], cnt = 0;
struct E{
int to, next;
}e[maxn << 1];
void add(int v, int u){
e[++cnt].next = h[u];
e[cnt].to = v;
h[u] = cnt;
}
int sz[maxn];
void dfs(int u, int par){
sz[u] = 1;
dp[u][0][0] = a[u];
dp[u][1][1] = 0;
for(int i = h[u] ; i != -1 ; i = e[i].next){
int v = e[i].to;
if(v == par) continue;
dfs(v, u);
for(int j = 0 ; j <= sz[u] + sz[v] ; ++j) tmp[j][0] = tmp[j][1] = inf;
for(int j = 0 ; j <= sz[u] ; ++j){
for(int k = 0 ; k <= sz[v] ; ++k){
tmp[j + k][0] = min(tmp[j + k][0], dp[u][j][0] + dp[v][k][0] + a[v]);
tmp[j + k][0] = min(tmp[j + k][0], dp[u][j][0] + dp[v][k][1]);
tmp[j + k][1] = min(tmp[j + k][1], dp[u][j][1] + dp[v][k][0]);
tmp[j + k][1] = min(tmp[j + k][1], dp[u][j][1] + dp[v][k][1]);
}
}
for(int j = 0 ; j <= sz[u] + sz[v] ; ++j){
dp[u][j][1] = tmp[j][1];
dp[u][j][0] = tmp[j][0];
}
sz[u] += sz[v];
}
}
ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }
int main(){
T = read();
while(T--){
memset(h, -1, sizeof(h));
n = read();
for(int i = 0 ; i <= n ; ++i){
for(int j = 0 ; j <= n ; ++j){
dp[i][j][0] = dp[i][j][1] = inf;
}
}
cnt = 0;
int x;
for(int i = 2 ; i <= n ; ++i){
x = read();
add(i, x), add(x, i);
}
for(int i = 1 ; i <= n ; ++i) a[i] = read();
dfs(1, 0);
for(int i = 0 ; i <= n ; ++i) printf("%lld ", min(dp[1][i][0], dp[1][i][1]));
printf("\n");
}
return 0;
}