• luogu P2014 选课(树形dp)


    传送门

    题意:

    现在有很多门课程,但是每门课程都会依赖某些其他的课程(即学了第(a_i)门课程之后才能学习第(a_{i+1})门课程)。每个课程都有相对应的学分,现在问你选取(m)个课程最多可以获得多少学分。

    分析:

    经典的树形依赖背包。根据题意,显然这样会形成一颗森林,而倘若我们把第(0)号节点作为假根,那么这样的话整张图就形成了一颗有根树。如果形成了这样的依赖状态的话,显然我们必须要先选择叶子结点的课程之后,才能够逐渐的往根部去学习,而现在在我们进行(dp)的过程中是符合这一条件的,现在我们需要考虑选和不选的状态。不难发现,这个选取(m)个课程的过程本质上跟背包的性质差不多,因此我们可以设(dp[x][k])为作为以(x)为根的子树,现在已经获取了(k)个课程后的最大的学分。而对于节点(x)选取了(k)个课程的状态(dp[x][k]),他显然是由他的儿子(son[x])选取了(j)个课程的状态(dp[son[x]][j])转移而来的,固有状态转移方程:(dp[x][k]=max(dp[x][k],dp[to][j]+dp[x][k-j]))

    而在这个过程中,我们需要递归一次,并每次枚举(j)(k),故总的时间复杂度为:(mathcal{O}(n^3))

    代码:

    #include <bits/stdc++.h>
    #define maxn 305
    using namespace std;
    struct Node{
        int to,next;
    }q[maxn];
    int head[maxn],cnt=0,val[maxn],dp[maxn][maxn];
    int n,m;
    void add_edge(int from,int to){
        q[cnt].to=to;
        q[cnt].next=head[from];
        head[from]=cnt++;
    }
    void init(){
        memset(head,-1,sizeof(head));
        cnt=0;
    }
    void dfs(int x){
        dp[x][1]=val[x];
        for(int i=head[x];i!=-1;i=q[i].next){
            int to=q[i].to;
            dfs(to);
            for(int j=m+1;j>=1;j--){
                for(int k=0;k<j;k++){
                    dp[x][j]=max(dp[x][j],dp[to][k]+dp[x][j-k]);
                }
            }
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        init();
        for(int i=1;i<=n;i++){
            int to;
            scanf("%d%d",&to,&val[i]);
            add_edge(to,i);
        }
        dfs(0);
        printf("%d
    ",dp[0][m+1]);
        return 0;
    }
    
    
  • 相关阅读:
    Python三维绘图--Matplotlib colorbar生成
    Python三维绘图--Matplotlib
    VIM剪切板的使用
    三维点云网络PointNet——模型及代码分析
    ECCV2018--点云匹配
    hdu 1905 小数化分数2
    hdu 1755 A Number Puzzle
    hdu 1796 How many integers can you find
    hdu 1452 Happy 2004
    hdu 2837 Calculation
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11205300.html
Copyright © 2020-2023  润新知