洛谷 P2014 选课(树形背包)

洛谷 P2014 选课(树形背包)

思路

题面:洛谷 P2014

如题这种有依赖性的任务可以用一棵树表示,因为一个儿子要访问到就必须先访问到父亲。然后,本来本题所有树是森林(没有共同祖先),但是题中的节点\(0\)其实就可以当做一个LCA,从节点\(0\)开始dp。

状态定义:\(dp[x][m]\)x节点,选则m课,获得的最大学分

决策时,类比背包,遍历每一个状态,用儿子的状态更新

dp转移方程(已优化一维):

\[dp[x][i] = max{dp[x][i-j]+dp[son(x)][j]} \]

这里需要注意的是,你定义的dp状态,是当前节点共选\(m\)课,而节点\(0\)是必须要选到的,所以应该一个选取\(m+1\)个课程,并且最终状态不是\(dp[0][m]\)而是\(dp[0][m+1]\)(卡了我好久……,所以定义dp状态时一定要自己清楚所代表的含义)

此题非常像洛谷 P1273 有线电视网,都是树形dp

代码

#include <cstdio>
#include <vector>
#define MAXN 303
#define INF 0x3fffffff
#define MAX(A,B) ((A)>(B)?(A):(B))
#define MIN(A,B) ((A)<(B)?(A):(B))
using namespace std;
int n,m,dp[MAXN][MAXN];
vector <int> mp[MAXN];
int dfs(int x){
    int cnt=1;
    for(register int i=0;i<mp[x].size();++i){
        int v=mp[x][i];
        int sz=dfs(v);
        cnt+=sz;
        for(register int j=m+1;j>=2;--j)
            for(register int k=0;k<=MIN(j-1, sz);++k)
                dp[x][j]=MAX(dp[x][j-k]+dp[v][k], dp[x][j]);
    }
    return cnt;
}
int main(){
    scanf("%d %d", &n, &m);
    for(register int i=1;i<=n;++i){
        int k,s;
        scanf("%d %d", &k, &s);
        dp[i][1]=s;
        mp[k].push_back(i);
    }
    dfs(0);
    printf("%d", dp[0][m+1]);
    return 0;
}
posted @ 2019-03-12 13:25  Santiego  阅读(179)  评论(0编辑  收藏  举报