一道经典树状 dp ## 题目描述:
在大学里的每个学生,为了达到一定的学分,
必须从很多课程里选择一些课程来学习,
有些课程必须在某些课程之前学习
现给出 n (n<=300) 个课程的先修课和学分,
在这 n 门课中选择 m (m<=300) 门学习,求能获得的最大学分
思路:
树状 dp
某一课程的先修课和这门课之间在树上构成了一种父子关系,
可以将这些课组织成一棵树,
并将这颗多叉树转为二叉树 (即记录这个结点的子结点以及下一个兄弟结点),
并构造一个虚拟结点 0 用来表示根结点
设 d (i,j) 为以 i 为根的子树,选择 j 门课的最优解
则答案为 d (son (0),m)
对于每个结点,枚举分配到兄弟和子结点的课程数
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int Mn(305); const int Mm(305); int n,m; int s[Mn]; int son[Mn],bro[Mn]; int d[Mn][Mm]; int f(int rt,int k) { if(d[rt][k]!=-1 return d[rt][k]; if(rt==0||k==0) return d[rt][k]=0; else { d[rt][k] = f(bro[rt],k); for(int i(0);i<k;++i) d[rt][k] = max(d[rt][k],f(son[rt],i)+f(bro[rt],k-i-1)+s[rt]); return d[rt][k]; } } int main() { cin >> n >> m; int cnt(0); for(int i(1);i<=n;++i) { int k; scanf("%d %d",&k,s+i); bro[i] = son[k]; son[k] = i; } memset(d,-1,sizeof d); cout << f(son[0],m); return 0; }
|