题面
题意
一个裸的树形背包问题
解析
缩点
从某个物品依赖的物品向这个物品连边,得到一个有向图。
这时我们会发现,在一个强连通分量里,如果想要有价值的话,必须全部选,根据贪心的思想,对于一个强联通分量,要么全部选,要么全部不选,所以我们可以把这幅有向图进行缩点。
因为每个物品只有一个依赖关系,所以每个强连通分量最多只会有一个父节点,也就是说,缩点后我们得到了森林。
为了方便进行树形背包,我们可以建一个根节点 0 0 0 号节点,把森林转化为一棵树。
背包
状态
树形背包的老套路:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示以
i
i
i 为根的子树,背包容量为
j
j
j 时的最大价值。
当前节点的状态(体积
v
v
v),自然是由当前节点较小的背包(体积
v
1
v_1
v1),加上子节点的背包(体积
v
2
v_2
v2)。满足:
v
=
v
1
+
v
2
v = v_1 + v_2
v=v1+v2
定义合法状态:
w
[
x
]
≤
v
w[x] \leq v
w[x]≤v
阶段
当前节点的背包容量,和子节点的背包容量。
注意外侧当前节点的背包容量采取倒叙循环,避免状态的重复转移。
决策
判断当前阶段是否合法,取最大值。
最后的答案自然就在 d p [ 0 ] [ m ] dp[0][m] dp[0][m] 中了,背包体积越大,自然价值就会相对更大。
小坑点
容易被样例误导,最开始的有向图中可能不会有 0 0 0 号节点的存在,因为物品之间可以互相依赖,所以还是要手动建 0 0 0 号节点。
代码
#include<cstdio>
#include<iostream>
using std:: min;
using std:: max;
const int N = 1e2 + 5,M = 5e2 + 5;
struct edge {
int next,to;
}a[N];
int head[N],n,m,a_size = 1,wei[N],v[N];
inline void add(int u,int v) {
a[++a_size] = (edge){head[u],v};
head[u] = a_size;
}
int dfn[N],low[N],val[N],w[N],c[N],sta[N],top = 0,cnt = 0,num = 0;
bool ins[N];
void tarjan(int x) {
dfn[x] = low[x] = ++num;
sta[++top] = x; ins[x] = true;
for(int i = head[x]; i; i = a[i].next) {
int y = a[i].to;
if(!dfn[y]) {
tarjan(y);
low[x] = min(low[x],low[y]);
}
else if(ins[y])
low[x] = min(low[x],dfn[y]);
}
if(dfn[x] == low[x]) {
cnt++; int y;
do {
y = sta[top--],ins[y] = false;
c[y] = cnt; w[cnt] += wei[y]; val[cnt] += v[y];
}while(x != y);
}
}
struct New {
int next,to;
}e[N];
int hc[N],e_size = 1,deg[N];
inline void add_c(int u,int v) {
e[++e_size] = (New){hc[u],v};
hc[u] = e_size;
} int dp[N][M];
void dfs(int x) {
for(int i = w[x]; i <= m; i++)
dp[x][i] = val[x];
for(int i = hc[x]; i; i = e[i].next) {
int y = e[i].to; dfs(y);
for(int j = m; j >= w[x]; j--)
for(int k = w[y]; k <= j; k++)
if(j - k >= w[x]) dp[x][j] = max(dp[x][j],dp[x][j - k] + dp[y][k]);
}
}
inline int read() {
int x = 0,flag = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')flag = -1;ch = getchar();}
while(ch >='0' && ch <='9'){x = (x << 3) + (x << 1) + ch - 48;ch = getchar();}
return x * flag;
}
int main() {
n = read(),m = read();
for(int i = 1; i <= n; i++)
wei[i] = read();
for(int i = 1; i <= n; i++)
v[i] = read();
for(int i = 1; i <= n; i++) {
int u = read();
if(u) add(u,i);
}
for(int i = 1; i <= n; i++)
if(!dfn[i]) tarjan(i);
for(int x = 1; x <= n; x++)
for(int i = head[x]; i; i = a[i].next) {
int y = a[i].to;
if(c[x] == c[y]) continue;
add_c(c[x],c[y]); deg[c[y]]++;
}
for(int i = 1; i <= cnt; i++)
if(!deg[i]) add_c(0,i);
dfs(0); printf("%d",dp[0][m]);
return 0;
}