【shopping题解】
10.17
思路:
考虑用树上动态规划来解决问题。
设f[u][k]表示以u为根的子树买k个物品的最小花费,且物品u使用了优惠卷;
设g[u][k]表示以u为根的子树买k个物品的最小花费,且物品u不使用优惠卷;
假设v是u的一个儿子结点,那么状态转移方程为:
f’[u][i + j] = min(f’[u][i + j], f[v][j] + f[u][i]);
f’[u][i + j] = min(f’[u][i + j], g[v][j] + f[u][i]);
g’[u][i + j] = min(g’[u][i + j], g[v][j] + g[u][i]);
理论复杂度O(n3)
树上动态规划的独特优化可以做到O(n2),详见参考代码,着重注意循环的界限以及size域的变化。
没有想到怎么把ans合并到root,于是就记忆化二分ans,感觉是不会挂的,但现实就是这么残酷。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010;
int n, b;
int siz[N], fa[N], c[N], d[N], f[N][N][2];
int main() {
freopen("shopping.in", "r", stdin);
freopen("shopping.out", "w", stdout);
scanf("%d%d", &n, &b);
for(int i=1; i<=n; i++) {
scanf("%d%d", &c[i], &d[i]);
if(i != 1) scanf("%d", &fa[i]);
}
memset(f, 63, sizeof(f));
for(int u=1; u<=n; u++) {
siz[u] = 1;
f[u][0][0] = 0;
f[u][1][0] = c[u];
f[u][1][1] = c[u] - d[u];
}
for(int v=n; v>=1; v--) {
if( fa[v] ) {
int u = fa[v];
for(int j=siz[u]; j>=0; j--) {
for(int k=1; k<=siz[v]; k++) {
f[u][j+k][0] = min(f[u][j+k][0], f[u][j][0] + f[v][k][0]);
f[u][j+k][1] = min(f[u][j+k][1], f[u][j][1] + f[v][k][0]);
f[u][j+k][1] = min(f[u][j+k][1], f[u][j][1] + f[v][k][1]);
}
}
siz[u] += siz[v];
}
}
int ans = 0;
for(int j=0; j<=siz[1]; j++) {
if(f[1][j][0] <= b) ans = max(ans, j);
if(f[1][j][1] <= b) ans = max(ans, j);
}
printf("%d\n", ans);
return 0;
}