可怜与超市
发现商品间的关系显然是棵树,所以做树上背包。
发现商品的价格都很高不能放在状态里,而价值都是1,所以转换维度。
定义(dp[u][j][0/1])为在(u)的子树中选出(j)个物品所需要的最小价格,其中0代表不用优惠券买u,1代表用优惠券买u。
转移:
[sum_{j = 0}^{siz[u] - siz[v]}
]
[sum_{k = 0}^{siz[v]}
]
[dp[u][j + k][1] = min(dp[u][j + k][1], dp[u][j][1] + min(dp[v][k][1], dp[v][k][0]))
]
[dp[u][j + k][0] = min(dp[u][j + k][0], dp[u][j][0] + dp[v][k][0])
]
答案为 (max(i)) 满足 (dp[1][i][1] <= b)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
char buf[1 << 20], *p1 = buf, *p2 = buf;
char getc() {
if (p1 == p2) {
p1 = buf, p2 = buf + fread(buf, 1, 1 << 20, stdin);
if (p1 == p2) return EOF;
}
return *p1++;
}
inline int read() {
int s = 0, w = 1;
char c = getc();
while (c < '0' || c > '9') { if (c == '-') w = -1; c = getc(); }
while (c >= '0' && c <= '9') s = s * 10 + c - '0', c = getc();
return s * w;
}
const int maxn = 5e3 + 5;
int n, B;
struct edge {
int nex, to;
}e[maxn * maxn];
int head[maxn], tot;
void Add(int u, int v) {
e[++tot] = (edge) { head[u], v };
head[u] = tot;
}
int dp[maxn][maxn][3];
int siz[maxn], c[maxn], d[maxn];
void Dfs(int u) {
siz[u] = 1;
for (int i = head[u]; i; i = e[i].nex) {
int v = e[i].to;
Dfs(v);
siz[u] += siz[v];
}
return;
}
void DP(int u) {
for (int i = head[u]; i; i = e[i].nex) {
int v = e[i].to;
DP(v);
for (int j = siz[u] - siz[v]; j >= 0; j--) {
for (int k = siz[v]; k >= 0; k--) {
dp[u][j + k][1] = min(dp[u][j + k][1], dp[u][j][1] + min(dp[v][k][1], dp[v][k][0]));
dp[u][j + k][0] = min(dp[u][j + k][0], dp[u][j][0] + dp[v][k][0]);
}
}
}
}
int main() {
n = read(), B = read();
int fa;
for (int i = 1; i <= n; i++) {
if (i == 1) {
c[i] = read();
d[i] = read();
}
else {
c[i] = read();
d[i] = read();
fa = read();
Add(fa, i);
}
}
Dfs(1);
memset(dp, 0x3f, sizeof dp);
for (int i = 1; i <= n; i++) {
dp[i][1][1] = c[i] - d[i];
dp[i][1][0] = c[i];
dp[i][0][0] = 0;
}
DP(1);
int ans;
for (int i = 1; i <= n; i++) {
if (dp[1][i][1] <= B)
ans = i;
}
printf("%d
", ans);
return 0;
}
另外,两个DFS可以合起来写,这样就相当于一次枚举一条边的两个端点,从各自的子树中去选择。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
char buf[1 << 20], *p1 = buf, *p2 = buf;
char getc() {
if (p1 == p2) {
p1 = buf, p2 = buf + fread(buf, 1, 1 << 20, stdin);
if (p1 == p2) return EOF;
}
return *p1++;
}
inline int read() {
int s = 0, w = 1;
char c = getc();
while (c < '0' || c > '9') { if (c == '-') w = -1; c = getc(); }
while (c >= '0' && c <= '9') s = s * 10 + c - '0', c = getc();
return s * w;
}
const int maxn = 5e3 + 5;
int n, B;
struct edge {
int nex, to;
}e[maxn * maxn];
int head[maxn], tot;
void Add(int u, int v) {
e[++tot] = (edge) { head[u], v };
head[u] = tot;
}
int dp[maxn][maxn][3];
int siz[maxn], c[maxn], d[maxn];
void DP(int u) {
siz[u] = 1;
for (int i = head[u]; i; i = e[i].nex) {
int v = e[i].to;
DP(v);
for (int j = siz[u]; j >= 0; j--) {
for (int k = siz[v]; k >= 0; k--) {
dp[u][j + k][1] = min(dp[u][j + k][1], dp[u][j][1] + min(dp[v][k][1], dp[v][k][0]));
dp[u][j + k][0] = min(dp[u][j + k][0], dp[u][j][0] + dp[v][k][0]);
}
}
siz[u] += siz[v];
}
}
int main() {
n = read(), B = read();
int fa;
for (int i = 1; i <= n; i++) {
if (i == 1) {
c[i] = read();
d[i] = read();
}
else {
c[i] = read();
d[i] = read();
fa = read();
Add(fa, i);
}
}
//Dfs(1);
memset(dp, 0x3f, sizeof dp);
for (int i = 1; i <= n; i++) {
dp[i][1][1] = c[i] - d[i];
dp[i][1][0] = c[i];
dp[i][0][0] = 0;
}
DP(1);
int ans;
for (int i = 1; i <= n; i++) {
if (dp[1][i][1] <= B)
ans = i;
}
printf("%d
", ans);
return 0;
}