[CODEVS1378]选课(树形dp)

本文介绍了一种使用动态规划解决子树中选择节点问题的方法。通过递归地从叶子节点向上计算最大值,实现了对以每个节点为根的子树内选择特定数量节点的最大价值的计算。

题目描述

传送门

题解

f[i][j]表示以i为根的子树选j个的最大值。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_n=305;
const int max_e=max_n*2;
int n,m,x,w[max_n],f[max_n][max_n];
int tot,point[max_n],next[max_e],v[max_e];
inline void add(int x,int y){++tot;next[tot]=point[x];point[x]=tot;v[tot]=y;}
inline void dp(int x){
    for (int i=point[x];i;i=next[i]){
      dp(v[i]);
      for (int j=m;j>=1;--j)
        for (int k=1; k<=j; k++)
          f[x][j]=max(f[x][j],f[x][j-k]+f[v[i]][k]);
    }
    if (x) for (int i=m; i>=1; i--) f[x][i]=f[x][i-1]+w[x];
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i) scanf("%d%d",&x,&w[i]),add(x,i);
    dp(0);
    printf("%d\n",f[0][m]);
}

总结

注意循环顺序,倒序循环保证更新的都是上一个状态。

### 关于选课问题的树形动态规划算法 #### 定义状态 定义 `dp[u][k]` 表示以节点 u 为根的子树中选择 k 门课程所能获得的最大学分。这里,u 是当前考虑的课程编号,而 k 则是从该课程及其后续依赖课程中选出的数量。 #### 初始化 初始化时,对于每一个叶子结点 v (即没有其他课程作为其前置条件),设置 `dp[v][0]=0, dp[v][1]=credit_v`,其中 credit_v 表示完成第v门课程可以获得的学分数[^1]。 #### 状态转移方程 当处理非叶节点 u 时,假设已经计算好了所有直接子节点的状态,则可以通过枚举分配给各个子树中的课程数量来进行更新: \[ \text{for } i=1\sim m_u,\quad \text{for } j=1\sim min(k-1,sum_{child(u)}) : \\ dp[u][i+j]\leftarrow max(dp[u][i+j],dp[u][i]+dp[child(u)][j]) \] 这里的 \(m_u\) 表示可以自由支配的选择数目(通常等于1加上剩余可选但未被占用的位置),\(sum_{child(u)}\) 表示所有孩子节点对应的子树大小之和[^2]。 #### 构建图结构并求解 构建一张有向无环图(DAG), 其中边的方向代表了先修关系;接着采用拓扑排序或者深度优先遍历(DFS)的方式自底向上地填充 DP 数组直到得到最终答案。 ```cpp #include<bits/stdc++.h> using namespace std; const int MAXN = ...; // Define according to problem constraints int n,m; vector<int> adj[MAXN]; pair<int,int> credits[MAXN]; // {pre_req, score} int dp[MAXN][MAXM]; void dfs(int node){ if(adj[node].empty()){ dp[node][0]=0; dp[node][1]=credits[node].second; return ; } for(auto child : adj[node]){ dfs(child); for(int i=m;i>=0;i--){ for(int j=0;j<=min(i-1,(int)adj[node].size());++j){ dp[node][i]=max(dp[node][i], dp[node][i-j]+dp[child][j]); } } } // Add current course's credit when choosing it. for(int i=1;i<=m;++i) dp[node][i]+=credits[node].second; } // Main function calls... ``` 此代码片段展示了如何通过 DFS 来解决这个问题,并利用二维数组存储中间结果以便回溯最优路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值