题目链接:https://www.acwing.com/video/472/
给定n门课,存在先修关系,构成一个森林,修一门课之前他的先修课程一定要完成,问在选m门课的情况下最多能获得多少学分?
如果没有先修规则就是一个裸的01背包问题。但这个问题不能用01背包解决。
而是一个分组背包问题,设f[x,j]为x为根的子树中修j门的最大学分数,从结点x出发,子树的数量就是分组的数量,背包的容量从0-j-1,每个分组中第k个物品的体积是k。
在每个点的子树处理完之后都要加上该点的分数,如果是虚拟节点x,选中之后加上的学分是0。在算体积为i时刻的学分数时用的是i-1时刻的最大学分数,所以要倒叙保证在计算i之前i-1没有改变。
代码:
#include<iostream> #include<cstdio> #include<vector> #include<cstring> using namespace std; const int maxn = 310; int n,m; int f[maxn][maxn]; int w[maxn]; vector<int> G[maxn]; void dfs(int x){ for(int i=0;i<G[x].size();i++){//物品 int y=G[x][i]; dfs(y); for(int j=m-1;j>=0;j--)//体积 for(int k=1;k<=j;k++){//决策 f[x][j]=max(f[x][j],f[x][j-k]+f[y][k]); } } if(x!=0) for(int i=m;i;i--) f[x][i]=f[x][i-1]+w[x];//加上必须课程,反向跌代 } int main(){ cin>>n>>m; int p; for(int i=1;i<=n;i++){ cin>>p>>w[i]; G[p].push_back(i); } m++;//加上了虚拟节点0,0也算一个 dfs(0); cout<<f[0][m]<<endl; return 0; }