选课
https://loj.ac/problem/10154
题目描述
给出\(n\)门课及其先修课,每门课有一定学分,求选\(m\)门课能获得的最大学分。
思路
由于原图可能是森林,可以先建一个虚根\(0\)号节点。我们考虑用\(f[i][j]\)表示以\(i\)为根的子树的选\(j\)门课能获得的最大学分,并且\(i\)这门课必选,那么对于\(f[i][j]\),其答案就是在每个子树中分别选\(k\)门课和其他子树中选\(j-k\)门课的答案。
代码
#include <bits/stdc++.h>
using namespace std;
vector<int>son[110];
int n,m,f[110][110],a[110];
void dfs(int u)
{
f[u][0]=0;
for(int i=0;i<son[u].size();i++)
{
int v=son[u][i];
dfs(v);
for(int j=n;j>=0;j--)
for(int k=j;k>=0;k--)
f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]);
}
if(u)
for(int i=n;i>=1;i--)
f[u][i]=f[u][i-1]+a[u];
}
int read()
{
int res=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
return res*w;
}
void write(int x)
{
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
void writeln(int x)
{
write(x);
putchar('\n');
}
int main()
{
m=read(),n=read();
for(int i=1;i<=m;i++)
{
int x=read();a[i]=read();
son[x].push_back(i);
}
dfs(0);
writeln(f[0][n]);
}