注意最里面那一层循环是在枚举物品:“选1个叶子节点”、“选2个叶子节点”……
因为最多只能选(cnt)个叶子节点((cnt)为当前子树中的叶子节点个数),一种优化是先统计出这个(cnt),最里面那层循环从1跑到(cnt)与(j)两者之间的最小值就可以结束了,都跑到(j)会TLE。
const int INF = 0x3f3f3f3f;
const int maxn = 3e3+10;
int dp[maxn][maxn];
int num = 0;
int head[maxn];
int n,m;
struct Edge {
int next, to;
int dis;
}edges[maxn];
void add_edge(int from, int to,int dis) {
num++;
edges[num].next = head[from];
edges[num].to = to;
edges[num].dis = dis;
head[from] = num;
}
int dfs(int u) {
if (u > n - m) return 1;
int sum = 0;
for (int i = head[u]; i; i = edges[i].next) {
int v = edges[i].to;
//第一层循环:枚举节点u的每一个儿子,一个儿子代表的子树为一组
int cnt = dfs(v);
sum += cnt;
//这是在数以u为根节点的子树有多少个叶子节点
for (int j = m; j >= 0; j--) {
//第二层循环:枚举背包容量
for (int k = 0; k <= min(cnt,j); k++) {
//第三层循环:枚举组内物品:选1个用户,选2个用户,选3个用户...
//显然最多只能选不超过j个用户且不超过cnt个用户,即最多选两者中的最小值
dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k] - edges[i].dis);
}
}
}
return sum;
}
//dp[i][j]:=在以i为根节点的子树中,选择j个叶子节点时的最大获利
int main()
{
//ios::sync_with_stdio(false);
//while (scanf("%d%d",&n,&m)!=EOF){
memset(dp, ~INF, sizeof(dp));
cin >> n >> m;
for (int i = 1; i <= n-m; i++) {
int k; cin >> k;
for (int j = 1; j <= k; j++) {
int v; int d;
cin >> v >> d;
add_edge(i, v, d);
}
}
for (int i = 1; i <= m; i++) cin >> dp[n - m + i][1];
for (int i = 1; i <= n; i++) dp[i][0] = 0;
dfs(1);
for (int i = m; i >= 1; i--) {
if (dp[1][i] >= 0) {
cout << i;
break;
}
}
return 0;
}