我们设
d
p
[
i
]
[
u
]
[
j
]
dp[i][u][j]
dp[i][u][j]表示以
u
u
u为祖先的子树上,前i个儿子满足j个用户的最大收益。
我们就可以写出转移方程,
d
p
[
i
]
[
u
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
u
]
[
v
]
,
d
p
[
i
−
1
]
[
v
]
[
j
−
k
]
+
d
p
[
u
当
前
儿
子
的
v
的
最
大
个
数
]
[
v
]
[
k
]
−
w
)
dp[i][u][j] = max(dp[i][u][v],dp[i-1][v][j - k] + dp[u当前儿子的v的最大个数][v][k] - w)
dp[i][u][j]=max(dp[i][u][v],dp[i−1][v][j−k]+dp[u当前儿子的v的最大个数][v][k]−w)
我们自底向上进行dfs,然后进行dp,这样我们可以将其转化为0/1背包,并且可以压缩一个维度,即
d
p
[
u
]
[
j
]
=
m
a
x
(
d
p
[
u
]
[
v
]
,
d
p
[
v
]
[
j
−
k
]
+
d
p
[
v
]
[
k
]
−
w
)
dp[u][j] = max(dp[u][v],dp[v][j - k] + dp[v][k] - w)
dp[u][j]=max(dp[u][v],dp[v][j−k]+dp[v][k]−w),注意
j
j
j要逆向循环,这样才能使得物品只选择一次。
二、ACcode
/*
* @Author: NEFU_马家沟老三
* @LastEditTime: 2020-09-24 20:05:15
* @CSDN blog: https://blog.csdn.net/acm_durante
* @E-mail: 1055323152@qq.com
* @ProbTitle:
*/#include<bits/stdc++.h>usingnamespace std;typedeflonglong ll;#define rep(i, a, n) for (int i = a; i <= n; i++)#define per(i, a, n) for (int i = n; i >= a; i--)#define lowbit(x) ((x) & -(x))#define lson l, mid, rt << 1#define rson mid + 1, r, rt << 1 | 1#define mem(a, b) memset(a, b, sizeof(a))#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);constdouble PI =acos(-1.0);int n,m;//n结点总数,m用户终端的数量int head[3005],cnt;struct node
{int v,w,next;}e[6007];int val[3005];voidadd(int u,int v,int w){
e[++cnt].v = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u]= cnt;}int dp[3005][3005];//以u为父亲节点,满足前i个用户的最大收益intdfs(int u,int fa){if(u > n - m){//用户节点
dp[u][1]= val[u];return1;}int leaves =0;//以u为父亲节点的树上,用户的数量for(int i = head[u];~i ; i = e[i].next){int v = e[i].v, w = e[i].w;if(fa == v)continue;int tmp =dfs(v,u);//以v节点为根的子树上用户的数量
leaves += tmp;for(int j = leaves ; j >0; j--){//状态-->背包数量->以父亲为子树下的用户数量for(int k =0;k <= tmp; k++){//决策-->选择k个用户if(j - k >=0)
dp[u][j]=max(dp[u][j], dp[u][j-k]+ dp[v][k]- w);}}}return leaves;}intmain(){mem(head,-1);mem(dp,-127);//初始化 scanf("%d%d",&n,&m);rep(i,1,n - m){int k,v,w;scanf("%d",&k);rep(j,1,k){scanf("%d%d",&v,&w);add(i,v,w);}}rep(i,n - m +1,n)scanf("%d",&val[i]);rep(i,1,n) dp[i][0]=0;//不选择用户的话就是0dfs(1,0);per(i,0,m){if(dp[1][i]>=0){printf("%d\n",i);break;}}return0;}