【模拟试题】选课 |
Time Limit:10000MS Memory Limit:65536K
Total Submit:365 Accepted:166
Case Time Limit:1000MS
Description
在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b)。一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少?
Input
第一行有两个整数N,M用空格隔开。(1<=N<=300,1<=M<=200)
接下来的N行,第I+1行包含两个整数ki和si, ki表示第I门课的直接先修课,si表示第I门课的学分。若ki=0表示没有直接先修课(1<=ki<=N, 1<=si<=20)。
Output
只有一行,选M门课程的最大得分。
Sample Input
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
Sample Output
13
Source
xinyue
题目:http://mail.bashu.cn:8080/bs_oj/showproblem?problem_id=1660
题意:你要在n门课中选出m门,使得获得的学分最大,当然有的课依赖于别的课先选。。。
分析:很容易想到在树上背包来解决问题,假设f[i][j]为以i为根的子树,包括i,选择j门课的最大值
那么有f[i][j]=max{ f[i][a]+f[k][b] }k是i的子树,a+b=j
这样的复杂度是O(n*m^2)对于这题的数据范围来说还是够用的,不过我用上了对这种泛化物品的背包的一种优化
复杂度降为O(n*m)
代码:
#include<cstdio> #include<iostream> using namespace std; const int mm=333; int f[mm][mm],k[mm],s[mm]; int i,j,n,m; void TreeDP(int u,int c) { if(c)for(int i=1,j;i<=n;++i) if(k[i]==u) { for(j=0;j<c;++j)f[i][j]=f[u][j]+s[i]; TreeDP(i,c-1); for(j=1;j<=c;++j) f[u][j]=max(f[u][j],f[i][j-1]); } } int main() { while(~scanf("%d%d",&n,&m)) { for(i=1;i<=n;++i) scanf("%d%d",&k[i],&s[i]); for(i=0;i<=m;++i)f[0][i]=0; TreeDP(0,m); printf("%d\n",f[0][m]); } return 0; }