题意
思考
这题的树形 (dp) 很明显,我们发现只可能有两种情况,要么是树要么是环,对于一个环来说肯定是要么都选要么都不选,可以缩点,缩完点之后我们得到了一个森林,不妨用一个源点将这些小树串成一个大树再来 (dp),后面就显然是一个树上有条件的分组背包了,由于必须要选当前点,所以强制选当前点再 (dp) 就好 ~
代码
#include<bits/stdc++.h>
using namespace std;
const int M = 550;
const int N = 110;
struct node{
int nxt, to;
}edge[N << 1], E[N << 1];
int head[N], num, H[N], num2;
void build(int from, int to){
edge[++num].nxt = head[from];
edge[num].to = to;
head[from] = num;
}
void build2(int from, int to){
E[++num2].nxt = H[from];
E[num2].to = to;
H[from] = num2;
}
int dfn[N], low[N], vis[N], S[N], col[N], sumv[N], sumw[N], val[N], w[N], du[N], top, cnt, color;
void tarjan(int u){
low[u] = dfn[u] = ++ cnt; S[++top] = u; vis[u] = 1;
for(int i=head[u]; i; i=edge[i].nxt){
int v = edge[i].to;
if(!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
else if(vis[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u]){
vis[u] = 0; col[u] = ++ color; sumv[color] += val[u]; sumw[color] += w[u];
while(S[top] != u){
int v = S[top];
vis[v] = 0; col[v] = color; sumv[color] += val[v]; sumw[color] += w[v];
top --;
}
top --;
}
}
int n, m;
int f[N][M], sz[N];
void dp(int u, int fa){
sz[u] = 1;
for(int i=sumw[u]; i<=m; i++) f[u][i] = sumv[u];
for(int i=H[u]; i; i=E[i].nxt){
int v = E[i].to;
if(v == fa) continue;
dp(v, u); sz[u] += sz[v];
for(int w=m-sumw[u]; w>=0; w--){
for(int k=0; k<=w; k++){
f[u][w + sumw[u]] = max(f[u][w + sumw[u]], f[u][w+sumw[u]-k] + f[v][k]);
}
}
}
}
int main(){
cin >> n >> m;
for(int i=1; i<=n; i++) cin >> w[i];
for(int i=1; i<=n; i++) cin >> val[i];
for(int i=1; i<=n; i++){
int d; cin >> d; if(d == 0) continue;
build(d, i);
}
for(int i=1; i<=n; i++){
if(!dfn[i]) tarjan(i);
}
for(int u=1; u<=n; u++){
for(int i=head[u]; i; i=edge[i].nxt){
int v = edge[i].to;
if(col[u] == col[v]) continue;
build2(col[u], col[v]); du[col[v]] ++;
}
}
for(int i=1; i<=color; i++){
if(du[i] == 0) build2(color + 1, i);
}
dp(color+1, 0);
cout << f[color+1][m];
return 0;
}
总结
注意细节吧