Luogu P2014 选课 题解报告

题目传送门

【题目大意】

有n门选修课,每一门课都有固定的学分$S_i$,每个学生可以选m门课。有些选修课有先修课,每一门课最多只有一门先修课,求能获得的最多学分。

【思路解析】

设f[x][t]表示在以x结点为根的子树中选t门课能获得的最大学分,x的子结点集合为son[x],子结点个数为p,且对于x的第i个子结点son[i],以其为根结点的子树中选课数量为$C_i$,则转移方程为:$$f[x][t]=max(sum_{i=1}^{p}f[son[i]][c[i]])+s[i](满足sum_{i=1}^{p}c[i]=t-1)$$这个方程其实是一个分组背包模型,有p组物品,每组物品有t-1个,其中第i组的第j个物品体积为j,价值为f[son[i]][j],背包的容积为t-1。

【代码实现】

 1 #include<bits/stdc++.h>
 2 #define rg register
 3 #define go(i,a,b) for(rg int i=a;i<=b;i++)
 4 #define back(i,a,b) for(rg int i=a;i>=b;i--)
 5 #define ll long long
 6 #define mem(a,b) memset(a,b,sizeof(a))
 7 using namespace std;
 8 const int N=302;
 9 vector<int> son[N];
10 int f[N][N],n,m,s[N];
11 void dp(int x){
12     f[x][0]=0;
13     int size=son[x].size();
14     go(i,0,size-1){//循环子结点(物品)
15         int y=son[x][i];
16         dp(y);
17         back(t,m,0) back(j,t,0)
18 //倒叙循环当前选课总门数(当前背包体积)
19 //循环更深子树上的选课门数(组内物品),此处使用倒序是为了正确处理组内体积为0的物品
20             if(t-j>=0) f[x][t]=max(f[x][t],f[x][t-j]+f[y][j]);
21     }
22     if(x!=0)//x不为0时,选修x本身要占掉一门课,并获得相应学分
23         back(t,m,1) f[x][t]=f[x][t-1]+s[x];
24     return;
25 }
26 int main(){
27     scanf("%d%d",&n,&m);
28     go(i,1,n){
29         int fa;
30         scanf("%d%d",&fa,&s[i]);
31         son[fa].push_back(i);
32     }
33     mem(f,0);
34     dp(0);
35     printf("%d
",f[0][m]);
36     return 0;
37 }
代码戳这里
原文地址:https://www.cnblogs.com/THWZF/p/11002226.html