• 洛谷P2014—选课(树形DP)


    题目

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

    输入输出

    输入:第一行输入N,M。下面N行输入两个整数ki和si,分别为第i门课的先修课序号,第i门课的学分。
    输出:选M门课的最大学分

    思路

    这道题输入的是森林,并不是一棵树,需要加一个结点,把每个子树连到根节点,构成一棵树,即加入0结点作为虚拟课程,学分为0。
    状态转移数组
    dp[i][j]为以i为根的子树选j门课程获得的最大学分。
    0结点是必选的,所以需要m+1。
    状态转移方程
    dp[u][i](i>=1)初始化为s[u]为u的学分。
    dp[u][j] = max(dp[u][j],dp[v][k] + dp[u][j-k]);

    不知道为啥代码显示有问题,

    #include <iostream>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    const int MAX_N = 310; //最多结点数
    int tot;    //标记边的序号
    int head[MAX_N],nxt[MAX_N],ver[MAX_N];  //建树要用到的数组
    int dp[MAX_N][MAX_N];
    int n,m,s[MAX_N];
    
    void addedge(int u,int v){  //根据邻接表建树的过程
        ver[++tot] = v;
        nxt[tot] = head[u];
        head[u] = tot;
    }
    
    void dfs(int u){
        dp[u][0] = 0;
        for(int i = 1;i < m;i++)  dp[u][i] = s[u];  //初始化都加上本身的学分
        for(int i = head[u];i;i = nxt[i]){
            int v = ver[i];
            dfs(v);
            for(int j = m;j >= 1;j--){  //逆序枚举背包容量
                for(int k = j - 1;k >= 0;k--){  //枚举给子树分配多少容量
                    dp[u][j] = max(dp[u][j],dp[v][k] + dp[u][j-k]);     //子树选k门课获得的学分加上本身选j-k门课获得的学分
                }
            }
        }
    }
    
    int main(){
    	cin >> n >> m;
    	int u;
    	for(int i=1;i <= n;i++){
            cin >> u >> s[i];
            addedge(u,i);
    	}
    	m++;   //将0这门虚拟课程也算进去了
    	dfs(0);
    	cout << dp[0][m] ;
    	return 0;
    }
    
    作者:inss!w!
    版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 相关阅读:
    语言基础
    进制转换
    Java基础相关
    Java基础了解
    php 条件查询和多条件查询
    php 增删改查练习
    php 用封装类的方法操作数据库和批量删除
    php 用面向对象的方法对数据库增删改查
    php 面向对象的方式访问数据库
    OOP 7大原则
  • 原文地址:https://www.cnblogs.com/Hfolsvh/p/15024560.html
Copyright © 2020-2023  润新知