Loading

洛谷P2014 选课(树形DP+分组背包)

题目描述

在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有 NNN 门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b)。一个学生要从这些课程里选择 MMM 门课程学习,问他能获得的最大学分是多少?

输入格式

第一行有两个整数 NNN , MMM 用空格隔开。( 1≤N≤3001 \leq N \leq 3001N300 , 1≤M≤3001 \leq M \leq 3001M300 )

接下来的 NNN 行,第 I+1I+1I+1 行包含两个整数 kik_i kisis_isi, kik_iki 表示第I门课的直接先修课,sis_isi 表示第I门课的学分。若 ki=0k_i=0ki=0 表示没有直接先修课(1≤ki≤N1 \leq {k_i} \leq N1kiN , 1≤si≤201 \leq {s_i} \leq 201si20)。

输出格式

只有一行,选 MMM 门课程的最大得分。

输入输出样例

输入 #1
7  4
2  2
0  1
0  4
2  1
7  1
7  6
2  2
输出 #1
13
#include <bits/stdc++.h>
using namespace std;
int n,m;
vector<int> v[305];//v[i]存储i的儿子节点 
int dp[305][305]={0};//dp[i][j]表示从i为根的子树里选j门课所取得的最大分数 
int score[305];//第i门课的分数为score[i] 
void dfs(int x)
{
    dp[x][0]=0;//显然啥课都不选 dp值为0 
    int i;
    for(i=0;i<v[x].size();i++)//枚举x的每个子节点 
    {
        int y=v[x][i];//y是子节点 
        dfs(y);//先dfs处理了子树 这样后面才能转移 
        int t,k;
        for(t=m;t>=0;t--)//背包容量 
        {
            for(k=0;k<=t;k++)//“在子节点中选k门课”构成一个物品 这里正序倒序都能ac 
            {
                if(t>=k)dp[x][t]=max(dp[x][t],dp[x][t-k]+dp[y][k]);//进行转移 选/不选取最大 
            }
        }
    }
    if(x!=0)//x不是虚拟节点的话 
    {
        int t;
        for(t=m;t>0;t--)//t不能取0 
        {
            dp[x][t]=dp[x][t-1]+score[x];//必然要加上老大 因为x是它子节点的先修课 所以t里面一定包含x这门课 
        }
    }
}
int main()
{
    cin>>n>>m;
    int i;
    for(i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(x==0)
        {
            v[0].push_back(i);
        }
        else v[x].push_back(i);
        score[i]=y;
    }
    dfs(0);//从虚拟节点开始处理 
    cout<<dp[0][m];//从0的子树里选m个课程 虚拟节点自然被忽略了 
    return 0;
}

注意虚拟零节点的使用以及分组背包的条件,物品泛化等。

posted @ 2020-03-23 11:55  脂环  阅读(191)  评论(0编辑  收藏  举报