zoukankan      html  css  js  c++  java
  • 选课【树形dp】

    树形dp入门经典题

    大意就是学每门课可以获得相应的学分 sc[ i ],但学一门课必须先学习他的先修课

    给定能学的课程数,求能得的最大学分。

    很容易想象出一个树形结构

    设 dp [ u ][ j ] 表示以 u 为根节点选 j 门课的学大得分。

    接下来我们可以选择学习 u 的子节点, 也可以继续选择子节点的子节点。

    我们枚举学习 u 的子节点的个数 k

    可得动态转移方程 

    dp[u][j] = max(dp[u][j], dp[u][j-k-1]+dp[v][k]+sc[v]);
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    typedef long long ll;
    using namespace std;
    const int maxn = 2000;
    int m, n;
    int dp[maxn][maxn], sc[maxn], fa[maxn];
    struct edge{
        int x, y, next;
    }a[maxn];
    int head[maxn], len = 0;
    void add(int x, int y){
        a[++len].x = x;
        a[len].y = y;
        a[len].next = head[x];
        head[x] = len;
    }
    int dfs(int u){
        int cnt = 0; //表示节点 u 下总共有多少门课
        for(int i=head[u]; i; i=a[i].next){ // 枚举 u 的子节点
            int v = a[i].y;
            cnt += dfs(v)+1;              // 更新课程数
            for(int j=min(cnt,m); j>=1; j--){ // 在 u 下选 j 门课, 01背包注意倒序
                for(int k=0; k<j; k++){  // 枚举选择子节点的子节点个数
                    dp[u][j] = max(dp[u][j], dp[u][j-k-1]+dp[v][k]+sc[v]);
                }
            }
        }
        return cnt;
    }
    int main(){
        scanf("%d%d", &m, &n);
        for(int i=1; i<=m; i++){
            scanf("%d%d",&fa[i],&sc[i]);
            add(fa[i],i);
        }
        dfs(0);
        printf("%d
    ",dp[0][n]);
        return 0;
    }

    这里还有一个技巧,如果没有先修课就让0和他连接,最后得到一棵0为根节点的数,dfs(0)即可

  • 相关阅读:
    用两个栈实现队列
    *重建二叉树
    *链表中环的入口结点
    *复杂链表的复制
    替换空格
    python多线程文件拷贝
    进程、线程、协程
    文件处理工具sed、awk
    CentOs软件安装
    python logging模块
  • 原文地址:https://www.cnblogs.com/hzoi-poozhai/p/12657666.html
Copyright © 2011-2022 走看看