选课树形dp
Posted hzoi-poozhai
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了选课树形dp相关的知识,希望对你有一定的参考价值。
树形dp入门经典题
大意就是学每门课可以获得相应的学分 sc[ i ],但学一门课必须先学习他的先修课
给定能学的课程数,求能得的最大学分。
很容易想象出一个树形结构
设 dp [ u ][ j ] 表示以 u 为根节点选 j 门课的学大得分。
接下来我们可以选择学习 u 的子节点, 也可以继续选择子节点的子节点。
我们枚举学习 u 的子节点的个数 k
可得动态转移方程
dp[u][j] = max(dp[u][j], dp[u][j-k-1]+dp[v][k]+sc[v]);
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> typedef long long ll; using namespace std; const int maxn = 2000; int m, n; int dp[maxn][maxn], sc[maxn], fa[maxn]; struct edge{ int x, y, next; }a[maxn]; int head[maxn], len = 0; void add(int x, int y){ a[++len].x = x; a[len].y = y; a[len].next = head[x]; head[x] = len; } int dfs(int u){ int cnt = 0; //表示节点 u 下总共有多少门课 for(int i=head[u]; i; i=a[i].next){ // 枚举 u 的子节点 int v = a[i].y; cnt += dfs(v)+1; // 更新课程数 for(int j=min(cnt,m); j>=1; j--){ // 在 u 下选 j 门课, 01背包注意倒序 for(int k=0; k<j; k++){ // 枚举选择子节点的子节点个数 dp[u][j] = max(dp[u][j], dp[u][j-k-1]+dp[v][k]+sc[v]); } } } return cnt; } int main(){ scanf("%d%d", &m, &n); for(int i=1; i<=m; i++){ scanf("%d%d",&fa[i],&sc[i]); add(fa[i],i); } dfs(0); printf("%d ",dp[0][n]); return 0; }
这里还有一个技巧,如果没有先修课就让0和他连接,最后得到一棵0为根节点的数,dfs(0)即可
以上是关于选课树形dp的主要内容,如果未能解决你的问题,请参考以下文章