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;
    }
    
  • 相关阅读:
    某个牛人做WINDOWS系统文件详解
    常用ASP脚本程序集锦
    LINUX基础:文件安全与权限
    proftpd+mysql+quota
    apache2.0.49tomcat5.0.19jk2建立virtualHost
    URL Redirection(转) Anny
    顶级域名后缀列表(转) Anny
    \u4E00\u9FA5意义 Anny
    How to POST Form Data Using Ruby(转) Anny
    How to get rid of 'Enter password to unlock your login keyring' in Ubuntu(转) Anny
  • 原文地址:https://www.cnblogs.com/57xmz/p/13489046.html
Copyright © 2011-2022 走看看