Karen and Supermarket
题意:
有
n
n
n个商品,第i个商品的价格为
c
i
c_i
ci美元,优惠劵
d
i
d_i
di美元,对于
i
≥
2
i \ge 2
i≥2,使用优惠劵的前提是必须先使用
x
i
x_i
xi的优惠劵。现问你
b
b
b美元最多能买多少个商品?
分析:
很明显,是一道有依赖的背包问题,树形背包。这也是我做的第一道树形背包。
我们设
d
p
[
u
]
[
c
n
t
]
[
0
/
1
]
dp[u][cnt][0/1]
dp[u][cnt][0/1]表示以
u
u
u为根的子树中,
u
u
u用或不用优惠劵选了
c
n
t
cnt
cnt个商品的最小费用。
仔细想想能得出状态转移方程如下:
- d p [ u ] [ i + j ] [ 0 ] = m i n ( d p [ u ] [ i + j ] [ 0 ] , d p [ u ] [ i ] [ 0 ] + d p [ v ] [ j ] [ 0 ] ) dp[u][i + j][0] = min(dp[u][i + j][0], dp[u][i][0] + dp[v][j][0]) dp[u][i+j][0]=min(dp[u][i+j][0],dp[u][i][0]+dp[v][j][0])
- d p [ u ] [ i + j ] [ 0 ] = m i n ( d p [ u ] [ i + j ] [ 0 ] , d p [ u ] [ i ] [ 1 ] + m i n ( d p [ v ] [ j ] [ 0 ] , d p [ v ] [ j ] [ 1 ] ) ) dp[u][i + j][0] = min(dp[u][i + j][0], dp[u][i][1] + min(dp[v][j][0], dp[v][j][1])) dp[u][i+j][0]=min(dp[u][i+j][0],dp[u][i][1]+min(dp[v][j][0],dp[v][j][1]))
我们 D F S DFS DFS的同时进行状态转移,最后我们只需要从 n n n往前枚举判断第一个符合 d p [ 1 ] [ i ] [ 0 ] < = b ∣ ∣ d p [ 1 ] [ i ] [ 1 ] < = b dp[1][i][0] <= b || dp[1][i][1] <= b dp[1][i][0]<=b∣∣dp[1][i][1]<=b的就是我们最多能买的商品数了。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e3 + 5;
struct Node {
int c, d, w;
Node(int c = 0, int d = 0, int w = 0):c(c),d(d),w(w){}
} a[maxn];
struct Edge {
int to, next;
Edge(int to = 0, int next = 0):to(to),next(next){}
} edges[maxn];
int tot = 1, head[maxn];
void addEdge(int u, int v) {
edges[++tot] = {v, head[u]};
head[u] = tot;
}
int dp[maxn][maxn][2]; // dp[i][j][0/1]表示以i为根的子树选j个物品,u用或不用优惠卷的最小代价
int son[maxn];
void dfs(int u) {
son[u] = 1;
dp[u][0][0] = 0;
dp[u][1][0] = a[u].c;
dp[u][1][1] = a[u].w;
for (int i = head[u], v; i; i = edges[i].next) {
v = edges[i].to;
dfs(v);
for (int j = son[u]; j >= 0; j--) {
for (int k = 0; k <= son[v]; k++) {
dp[u][j + k][0] = min(dp[u][j + k][0], dp[u][j][0] + dp[v][k][0]);
dp[u][j + k][1] = min(dp[u][j + k][1], dp[u][j][1] + min(dp[v][k][1], dp[v][k][0]));
}
}
son[u] += son[v];
}
}
int main() {
int n, b;
memset(dp, 0x3f, sizeof(dp));
scanf("%d%d", &n, &b);
for (int i = 1, j; i <= n; i++) {
scanf("%d%d", &a[i].c, &a[i].d);
a[i].w = a[i].c - a[i].d;
if (i > 1) {
scanf("%d", &j);
addEdge(j, i);
}
}
dfs(1);
for (int i = n; i >= 0; i--) if (dp[1][i][1] <= b || dp[1][i][0] <= b) return printf("%d\n", i), 0;
return 0;
}