「CTSC1997」选课

Description

在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有 N 门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b)。一个学生要从这些课程里选择 M 门课程学习,问他能获得的最大学分是多少?

Input

第一行有两个整数 N , M 用空格隔开。(1≤N≤300,1≤M≤300 )

接下来的 N 行,第 I+1 行包含两个整数 ki 和 si,ki 表示第I门课的直接先修课,s表示第I门课的学分。若 ki=0 表示没有直接先修课(1≤ki≤N,1≤si≤20)。

Output

只有一行,选 M 门课程的最大得分。

Solution

因为每门课的的先修课最多只有一门(对应着树中每个结点至多只有1个父结点),所以这N门课程构成了森林结构(若干棵树,因为可能有不止一门课没有先修课)。为简便起见,我们可以新建一个“虚拟课程”——0号结点,作为“实际上没有先修课的课程”的先修课,把包含N个结点的森林转化为包含N+1个结点的树,其中结点0为根结点。

设f[x][t]表示在以x为根的子树中选t门课能够获得的最高学分,设x的子结点集合为Son(x),子结点个数p=|Son(x)|。修完x这门课后,对于x的每个子结点y∈Son(x),我们可以在以y为根的子树中选修若干门课(记为ci),在满足Σci=t-1的基础上获得尽量多的学分。

当t=0时,显然f[x][t]=0。当t>0时,根据以上分析,状态转移方程如下:

$f[x][t]=max{\sum_{i=1}^{p}f[y_i][c_i]}+score[x]$

需满足 $\sum_{i=1}^{p} c_i=t-1$

这个方程其实是一个分组背包模型(瞎猜)。有 P=|Son(x)| 组物品,每组物品都有 t-1,其中第i组的第j个物品的体积为j,价值为f[y][j],背包的总容积为t-1。我们要从每组中选出不超过1个物品(每个子结点y只能选一个状态转移到x),使得物品体积不超过t-1的前提下(在修完x后,还能选修t-1门课),物品价值总和最大(获得最多学分)。当然,x=0是一个特例,因为虚拟的根结点实际上不需要被选修,此时背包总体积应为t。我们用分组背包进行树形DP的状态转移。

扫描二维码关注公众号,回复: 10101745 查看本文章

按照这个做法乱搞一下就AC啦我白嫖了

code

放个奇怪的代码叭

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 const int N=310;
 5 int n,m,x,f[N][N],s[N];
 6 vector<int>son[N];
 7 void dp(int x){
 8     f[x][0]=0;
 9     for(int i=0;i<son[x].size();i++){
10         int y=son[x][i]; dp(y);
11         for(int t=m;t>=0;t--)
12             for(int j=t;j>=0;j--)
13                 f[x][t]=max(f[x][t],f[x][t-j]+f[y][j]);
14     }
15     if(x!=0) for(int t=m;t>0;t--) f[x][t]=f[x][t-1]+s[x];
16 }
17 signed main(){
18     //freopen(".in","r",stdin);
19     //freopen(".out","w",stdout);
20     scanf("%lld%lld",&n,&m);
21     for(int i=1;i<=n;i++){
22         scanf("%lld%lld",&x,&s[i]);
23         son[x].push_back(i);
24     }
25     dp(0),printf("%lld\n",f[0][m]);
26     return 0;
27 } 

 此蒟蒻不会树形dp,有错欢迎更正

顺便说一句:杜老师天下第一

猜你喜欢

转载自www.cnblogs.com/maoyiting/p/12555368.html