zoukankan      html  css  js  c++  java
  • 洛谷P2014 [CTSC1997]选课

    题目描述

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

    输入格式

    第一行有两个整数 (N) , (M) 用空格隔开。( (1 leq N leq 300) , (1 leq M leq 300) )

    接下来的 (N) 行,第 (I+1) 行包含两个整数 (k_i)(s_i) , (k_i) 表示第(I)门课的直接先修课,(s_i) 表示第I门课的学分。若 (k_i=0) 表示没有直接先修课((1 leq {k_i} leq N) , (1 leq {s_i} leq 20))。

    输出格式

    只有一行,选 (M) 门课程的最大得分。

    输入输出样例

    输入 #1

    7  4
    2  2
    0  1
    0  4
    2  1
    7  1
    7  6
    2  2
    

    输出 #1

    13
    

    思路

    这个题确实比较明显是一道树形dp,课与先修课之间的关系很明显是父节点与子节点之间的关系。用二维数组(f_{i,j})表示以(i)为根节点的子树选(t)门课的最大得分。下面考虑状态转移方程,然后会猛然发现,这好像是一个分组背包?!(t)门课相当于背包的容积,而子树就是分组。

    但是dp要有根节点,我们并不知道选哪个课会最优,也就是无法确定根节点,那么我们就自己设一个虚拟根节点0,作为实际没有先修课的课程的先修课。这样0号结点就成为了整棵树的根节点,从0开始dp即可。

    代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<cstdlib>
    #include<ctime>
    using namespace std;
    int n,m,score[310];
    vector<int> son[310];
    int fa[310];
    int f[310][310];//f[x][t]表示在以x为根的子树内选t门课程的最大得分 
    void dp(int x){
    	f[x][0]=0;//选0门课肯定得分为0 
    	for(int i=0;i<son[x].size();i++){
    		int y=son[x][i];
    		dp(y);//遍历子树并递归 
    		for(int t=m;t>=0;t--){//循环背包容积
    			for(int j=t;j>=0;j--){//循环组内物品
    				f[x][t]=max(f[x][t],f[x][t-j]+f[y][j]);
    			}
    		}
    	}
    	if(x!=0){//如果不是虚拟的0结点,那么还要更新
    		for(int t=m;t>0;t--){
    			f[x][t]=f[x][t-1]+score[x];
    		}
    	}//如果到了叶子结点那么只有这一层循环,相当于背包所以倒着循环。如果不是叶子结点也要更新,因为根节点必须选 
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	memset(fa,-1,sizeof(fa));
    	for(int i=1,op;i<=n;i++){
    		scanf("%d%d",&op,&score[i]);
    		if(op!=0){
    			son[op].push_back(i);//建树 
    			fa[i]=op;
    		}
    	}
    	for(int i=1;i<=n;i++){
    		if(fa[i]==-1){
    			son[0].push_back(i);//找到根节点 
    			fa[i]=0;
    		}
    	}
    	dp(0);//dp 
    	printf("%d
    ",f[0][m]);//输出以虚拟结点0为根节点的子树选m门课的最大得分 
    	return 0;
    }
    
  • 相关阅读:
    全排列(求所有情况的个数)--10--全排列--蓝桥杯凑算式和leetcode46全排列
    DFS+BFS(广度优先搜索弥补深度优先搜索遍历漏洞求合格条件总数)--09--DFS+BFS--蓝桥杯剪邮票
    Powermock2.0.0 详细 总结
    Springboot 前后端数据传输 常见误区
    idea src下源文件和class编译文件不一致
    java对象clone
    数据结构-链表
    队列
    稀疏数组
    数据库隔离级别
  • 原文地址:https://www.cnblogs.com/57xmz/p/13489046.html
Copyright © 2011-2022 走看看