zoukankan      html  css  js  c++  java
  • 选课(背包类树形DP)

    传送门

    题意:有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课则只有学了课程a,才能学习课程b),从这些课程里选择M门课程学习,求能获得的最大学分?

    分析:因为每门课的直接先修课最多只有一门,相当于树中每个节点最多只有一个父节点.因为可能有多门课没有先修课,所以这N门课程构成了一片森林.因为森林结构不方便处理,我们建立一个0号节点作为虚拟课程,表示所有实际上没有先修课的课程的先修课,于是N个节点的森林变成了N+1个节点的有根树,其中0号节点是根节点.

    既然树形结构已经构建出来了,题目又是要求最优解,应该不难想到树形DP.设f[x][j]表示在以x为根的子树中选j门课能够获得的最大学分(提醒一下,这里x也视作以x为根的树上的子节点).

    设x一共有p个子节点,修完x这门课后,对于x的每个子节点y,我们可以在以y为根节点的子树中选修ci门课,满足((sum_{i=1}^{p}c[i])=j-1).

    当j=0时,显然有f[x][0]=0.

    当j>0时,(f[x][j]=(max{sum_{i=1}^{p}[yi][ci]})+s[x]),其中((sum_{i=1}^{p}c[i])=j-1).

    int n,m;
    int s[305],f[305][305];
    vector<int> q[305];
    //从根节点0开始DFS遍历直到叶节点,
    //然后从叶节点层层向上更新信息,这是一般树形DP的套路
    void dfs(int x){
        f[x][0]=0;//初始化
        for(int i=0;i<q[x].size();i++){
    		int y=q[x][i];
    //y是x的子节点,即学了x才能学y
    		dfs(y);//层层DFS下去
    		for(int j=m;j>=0;j--)//在以x为根节点的树上学j门课程
    	    for(int k=j;k>=0;k--)//在x的当前子树y上学k门
    			f[x][j]=max(f[x][j],f[x][j-k]+f[y][k]);
        }
        if(x!=0){//x不为0时,选修x本身占用了1门课
    		for(int j=m;j>=1;j--)
    	    	f[x][j]=f[x][j-1]+s[x];
        }
    }
    int main(){
        n=read();m=read();
        for(int i=1;i<=n;i++){
    		int k=read();s[i]=read();
    		q[k].push_back(i);//便于DFS建树
        }
        for(int i=0;i<=n;i++)
    		for(int j=0;j<=m;j++)
    	    	f[i][j]=-1e9;
    //求最大值,初值赋为无穷小
        dfs(0);
        printf("%d
    ",f[0][m]);
        return 0;
    }
    
    
  • 相关阅读:
    友盟上报 IOS
    UTF8编码
    Hill加密算法
    Base64编码
    Logistic Regression 算法向量化实现及心得
    152. Maximum Product Subarray(中等, 神奇的 swap)
    216. Combination Sum III(medium, backtrack, 本类问题做的最快的一次)
    77. Combinations(medium, backtrack, 重要, 弄了1小时)
    47. Permutations II(medium, backtrack, 重要, 条件较难思考)
    3.5 find() 判断是否存在某元素
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10368620.html
Copyright © 2011-2022 走看看