题解Luogu P2014 选课

Posted lizbaka

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解Luogu P2014 选课相关的知识,希望对你有一定的参考价值。

Problem

树上背包问题的典例,记下来


solution

(dp[x][t])表示以(x)为子树,选(t)门课获得的最大学分

(p)(x)的子节点数量,(c_i)(x)的子节点(y_i)选修的课数

转移方程如下

[dp[x][t]=max_{sum_{i=1}^pc_i=t-1}{sum_{i=1}^pdp[y_i][c_i]}+pnt[x]]

事实上,这是一个分组背包模型,对于每个节点(x),每个子节点(y_i)是一个组,在其中选取不超过(1)个元素(c_i)加入背包。将当前枚举到的组作为阶段

对于没有先修课的课程,我们可以将一个超级根节点(0)作为它们的父节点,方便计算

Code

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <vector>
#define maxn 305
#define maxm 305
using namespace std;
typedef long long ll;

int n,m;
vector<int> son[maxn];
int prt[maxn];
int pnt[maxn];
int dp[maxn][maxm];

void DP(int u)
{
    for(register int i=0;i<son[u].size();++i)//阶段,选取了第几组
    {
        int v=son[u][i];
        DP(v);
        for(register int t=m;t>=0;--t)//枚举背包已经放入的体积
            for(register int j=t;j>=0;--j)//枚举组内加入的物品,枚举顺序在本题中并无影响,倒叙枚举原因待探究
                dp[u][t]=max(dp[u][t],dp[u][t-j]+dp[v][j]);
    }
    if(u!=0)//除超级根节点外,每个节点的选取都会获得pnt[u]的学分
        for(register int t=m;t>0;--t)
            dp[u][t]=dp[u][t-1]+pnt[u];
}

int main()
{
    scanf("%d%d",&n,&m);
    int k;
    for(register int i=1;i<=n;++i)
    {
        scanf("%d%d",&k,&pnt[i]);
        prt[i]=k;
    }
    for(register int i=1;i<=n;++i)
        son[prt[i]].push_back(i);
    DP(0);
    printf("%d",dp[0][m]);
    return 0;
}

以上是关于题解Luogu P2014 选课的主要内容,如果未能解决你的问题,请参考以下文章

MZOJ 1132 && LuoGu P2014 选课

选课[Luogu P2014]

Luogu P2014 选课

Luogu P2014选课

Luogu P2014 选课 (树形DP)

LuoGu P2014选课(人生第一个树上背包)