传送门:牛客
题目描述:
学校实行学分制。
每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。
学校开设了 N 门的选修课程,每个学生可选课程的数量 M 是给定的。
学生选修了这 M 门课并考核通过就能获得相应的学分。
在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其他的一些课程的基础上才能选修。
例如《Windows程序设计》必须在选修了《Windows操作基础》之后才能选修。
我们称《Windows操作基础》是《Windows程序设计》的先修课。
每门课的直接先修课最多只有一门。
两门课可能存在相同的先修课。
你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修条件。
假定课程之间不存在时间上的冲突。
输入:
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
输出:
13
一道经典的树状背包dp题,与这道题的方法是一模一样的,所以关于树形dp的事情在这里就不在赘述了,可以去那边看一看
下面讲一下不一样的部分:
对于这道题我们给出的关系可能并不是一颗树,所以我们需要建立一个超级源点(在这里可以选择0),然后将其合并成一个森林即可(将我们的m+1,因为我们的超级源点并不需要花费),然后直接套我们的树形背包dp即可
下面是具体的代码部分:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <stack>
#include <deque>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
int n,m;int val[maxn];int dp[1000][1000];
vector<int>tree[maxn];
void dfs(int u) {
for(int i=1;i<=m;i++) dp[u][i]=val[u];
for(int i=0;i<tree[u].size();i++) {
int v=tree[u][i];
dfs(v);
for(int j=m;j>=1;j--) {
for(int k=1;k<j;k++) {
dp[u][j]=max(dp[u][j],dp[u][k]+dp[v][j-k]);
}
}
}
}
int main() {
n=read();m=read();
int u;
for(int i=1;i<=n;i++) {
u=read();
tree[u].push_back(i);
val[i]=read();
}
m++;
dfs(0);
cout<<dp[0][m]<<endl;
return 0;
}