zoukankan      html  css  js  c++  java
  • P2014 选课

    P2014 选课

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

    Solution

    挖了好久的坑啊终于搞懂填上了
    经典的树上背包
    我们以此题为例, 和背包问题做一个类比

    首先我们明确一下最后这颗树会变成什么样
    把不要的部分删掉, 我们发现最后居然还是一颗树, 并且这棵树没有被肢解分开
    可以自己画个图理解一下

    好了上面其实没什么东西。。, 助消化而已
    类比:
    对于一个节点 (u) 的子节点(或者叫做以 (v) 为根的子树, 大小为 (t)(v) , 若是我们想在这颗子树中选一段放入背包(先不考虑背包大小限制), 那么我们有 (t +1) 种方案, 即:
    选择体积为 (0, 1, 2, ..., t) 的子树, 且选择互斥(不能又选大小为 (2) 的又选大小为 (1) 的)
    然后抽象到背包上:
    (u) 的每颗子树 (v) ---> 每组物品
    (v) 中体积为 (0, 1, 2, ..., t) 的子树 ---> 组内物品
    典型的分组背包模型

    在本题中, 因为可能存在森林, 我们添加一个虚节点 (0) 总根, 处理的时候分情况讨论即可
    细节注释中

    Code

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define LL long long
    #define REP(i, x, y) for(int i = (x);i <= (y);i++)
    using namespace std;
    int RD(){
        int out = 0,flag = 1;char c = getchar();
        while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
        while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
        return flag * out;
        }
    const int maxn = 100019,INF = 1e9 + 19;
    int head[maxn],nume = 1;
    struct Node{
        int v,dis,nxt;
        }E[maxn << 3];
    void add(int u,int v,int dis){
        E[++nume].nxt = head[u];
        E[nume].v = v;
        E[nume].dis = dis;
        head[u] = nume;
        }
    int num, m;
    int v[maxn];
    int dp[maxn][319];
    int dfs(int u, int F){
    	int size = 1;
    	dp[u][0] = 0;
    	for(int i = head[u];i;i = E[i].nxt){
    		int v = E[i].v;
    		if(v == F)continue;
    		int t = dfs(v, u);//t为这组物品的物品数
    		size += t;
    		for(int j = m;j >= 0;j--){//枚举体积
    			REP(k, 0, t){//枚举组内物品, 即体积为0, 1, 2, 3 ,... t的物品
    				if(j >= k)
    					dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k]);
    				}
    			}
    		}
    	if(u)
    		for(int i = min(size, m);i >= 1;i--)
    			dp[u][i] = dp[u][i - 1] + v[u];//自己必选,倒叙防止v[u]累计
    	return size;
    	}
    int main(){
    	num = RD(), m = RD();
    	REP(i, 1, num){
    		int pre = RD(), score = RD();
    		v[i] = score;
    		add(i, pre, 1), add(pre, i, 1);
    		}
    	dfs(0, -1);
    	printf("%d
    ", dp[0][m]);
    	return 0;
    	}
    
  • 相关阅读:
    tty初探 — uart驱动框架分析
    是否要从单片机转为嵌入式Linux?
    Linux 下Input系统应用编程实战
    Linux设备驱动之Kobject、Kset
    Xorg-xserver相关知识
    linux各级目录
    GitHub使用基本流程
    6、Linux发行版组成与初识
    CentOS7安装出现Warning
    Python数据类型之变量
  • 原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9792122.html
Copyright © 2011-2022 走看看