• CH5402 选课


    https://ac.nowcoder.com/acm/contest/1044/B

    题目

    学校实行学分制。

    每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。

    学校开设了 N 门的选修课程,每个学生可选课程的数量 M 是给定的。

    学生选修了这 M 门课并考核通过就能获得相应的学分。

    在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其他的一些课程的基础上才能选修。

    例如《Windows程序设计》必须在选修了《Windows操作基础》之后才能选修。

    我们称《Windows操作基础》是《Windows程序设计》的先修课。

    每门课的直接先修课最多只有一门。

    两门课可能存在相同的先修课。

    你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修条件。

    假定课程之间不存在时间上的冲突。

    题解

    设$dp[r][c]$为以$r$为根,选择$c$门课的最多学分

    那么

    [dp[r][c]=maxleft{dp[s_i][c_i]|(sum_{i=1}^{p} c_i)=c-1}+v[r]]

    然而这个式子看起来像是废话,只靠这个是不能写出时限内的转移的($O(2^n)$)

    还是需要利用背包,考虑某个节点的n个子树,把上一个式子扩展一下

    [dp[r][cnt][c]=max{dp[r][cnt-1][c-c_i]+dp[s_i][n_s][c_i]}+v[r]],可以滚动优化[cnt],枚举$c_s$,时间复杂度$O(n*m*m)$

    AC代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define REP(i,a,b) for(register int i=(a); i<(b); i++)
    #define REPE(i,a,b) for(register int i=(a); i<=(b); i++)
    #define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
    using namespace std;
    int n,m;
    int hd[307], nxt[307], to[307], e=0, v[307];
    inline void adde(int a, int b) { nxt[e]=hd[a]; hd[a]=e; to[e]=b; e++; }
    int dp[307][307];
    void calc(int r) {
    	for(int i=hd[r]; ~i; i=nxt[i]) {
    		int s=to[i];
    		calc(s);
    		PERE(c,m,0)
    		REPE(k,0,c) {
    			dp[r][c]=max(dp[r][c],dp[r][c-k]+dp[s][k]);
    		}
    	}
    	if(r) PERE(c,m,1) {
    		dp[r][c]=dp[r][c-1]+v[r];
    	}
    }
    int main() {
    	memset(dp,0,sizeof(dp));
    	memset(hd,-1,sizeof hd);
    	scanf("%d%d", &n, &m);
    	REPE(i,1,n) {
    		int a;
    		scanf("%d%d", &a, &v[i]);
    		adde(a,i);
    	}
    	calc(0);
    	printf("%d
    ", dp[0][m]);
    }
    
  • 相关阅读:
    206.反转链表
    gprof
    Java【Stream流、方法引用】学习笔记
    Java【函数式接口(Supplier、Comsumer、Predicate、Function)】学习笔记
    Python exec 内置语句
    Python sorted() 函数
    Python oct() 函数
    Python id() 函数
    Python dir() 函数
    软件测试的方法
  • 原文地址:https://www.cnblogs.com/sahdsg/p/12459730.html
Copyright © 2020-2023  润新知