• b_lg_选课 & 苹果树 & 树 & 树上染色(树形分组背包)


    学校开设了 N 门的选修课程,每个学生可选课程的数量 M 是给定的。
    学生选修了这 M 门课并考核通过就能获得相应的学分。
    在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其他的一些课程的基础上才能选修。
    例如《Windows程序设计》必须在选修了《Windows操作基础》之后才能选修。
    每门课的直接先修课最多只有一门。两门课可能存在相同的先修课。
    你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修条件。假定课程之间不存在时间上的冲突。
    输入格式
    输入文件的第一行包括两个整数N、M(中间用一个空格隔开)其中1≤N≤300,1≤M≤N。
    接下来N行每行代表一门课,课号依次为1,2,…,N。
    每行有两个数(用一个空格隔开),第一个数为这门课先修课的课号(若不存在先修课则该项为0),第二个数为这门课的学分。
    学分是不超过10的正整数。
    输出格式
    输出一个整数,表示学分总数。

    输入样例:
    7 4
    2 2
    0 1
    0 4
    2 1
    7 1
    7 6
    2 2
    输出样例:
    13
    

    方法一:dp

    这里可将课的看成重量为 1 的物品,对应的价值就是其学分,再利用分组背包思想;由于入度为 0 的先修课(节点)没有父节点,而这些课又必须上,故增加一个虚拟结点 0,0 指向这些入度为 0 的结点,且结点 0 的学分为 0

    • 定义状态
      • f[i][j] 表示以 i 为根选 j 门课时收获的最大学分
    • 思考初始化:
      • f[...][...]=0
    • 思考状态转移方程
      • f[i][j] = max{f[i][j], f[i][k]+f[son_i][j-k]},j∈[0,m),k∈[1,j);以 i 为根的子树要选 j 门课的最大学分等于 i 的子树选 j 门,加上从 son_i 的及其子树中选 j-k 门课的最大学分
    • 思考输出:f[0][m]
    #include<bits/stdc++.h>
    using namespace std;
    const int N=305;
    int n,m,v[N],f[N][N];
    vector<int> g[N];
    
    void dfs(int u) {
        for (int v : g[u]) {
            dfs(v);
            for (int j=m; j>=1; j--)
            for (int k=1; k<=j; k++) {
                f[u][j]=max(f[u][j], f[u][j-k]+f[v][k]);
            }
        }
        for (int j=m; j>=1; j--) f[u][j]=f[u][j-1]+v[u];  //选u这门课
    }
    
    int main() {
        std::ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        cin>>n>>m;
        for (int i=1; i<=n; i++) {
            int p; cin>>p>>v[i];
            g[p].push_back(i);
        }
        m++;
        dfs(0);
        cout << f[0][m];
        return 0;
    }
    

    复杂度分析

    • Time\(O(nms)\)
    • Space\(O(m)\)

    二、苹果树

    有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)
    这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。
    现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。
    给定需要保留的树枝数量,求出最多能留住多少苹果。

    f[i][j] 表示保留以i为根的子树的j条树枝可以留下来多少苹果,那么假如i保留了p条树枝,则i的子节点只能保留q-p-1条(q是最终需要保留的树枝数),减1是因为选择了 \(i->son_i\) 这一条

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=105;
    struct node{
        int u,w;
    };
    vector<node> g[N];
    int n,q,vis[N],f[N][N];
    
    void dfs(int u) {
        vis[u]=1;
        for (auto& to : g[u]) {
            int v=to.u, w=to.w;
            if (vis[v]) continue;
            dfs(v);
            for (int j=q; j>=1; j--)
            for (int k=0; k<j; k++) {
                f[u][j]=max(f[u][j], f[u][j-k-1]+f[v][k]+w);
            }
        }
    }
    int main() {
        std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
        cin>>n>>q;
        for (int i=0; i<n; i++) {
            int a,b,c; cin>>a>>b>>c;
            g[a].push_back({b,c}), g[b].push_back({a,c});
        }    
        dfs(1);
        cout<<f[1][q];
        return 0;
    }
    

    三、树

    他从树中删除任意数量(可以为 00)的边,计算删除后所有连通块大小的乘积,L 将得到这么多的分数。
    你的任务就是对于一颗给定的树,求出 L 能得到的最大分数。
    输入格式
    第一行一个整数 nn,表示树的节点个数。
    接下来 (n-1)(n−1) 行,每行两个整数 u, vu,v,代表存在一条连接 u, vu,v 的边。
    输出格式
    输出一行一个整数,表示 L 能得到的最大分数。

    
    

    四、树上染色

    有一棵点数为 n 的树,树边有边权。给你一个在 0∼n 之内的正整数 k ,你要在这棵树中选择 k 个点,将其染成黑色,并将其他 的 n−k 个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益,问受益最大值是多少。

    f[u][j]=max(f[u][j], f[u][j-k]+f[v][k]+val),大同小异的题,难点在于如何计算这个 val(白点两两之间的距离+黑点两两之间的距离)

    
    
  • 相关阅读:
    根据用户输入的时间查询那天的数据
    动软 生成 linq相关DAO
    pdf 移除密码 去除水印 批量去除水印 编辑文字 批量替换文字
    利用OCR识别扫描的jpg、tif文件的文字
    jstat命令详解
    IDEA自动编译设置
    IntelliJ IDEA:Field injection is not recommended
    阿里巴巴Druid数据库连接池配置详解及使用
    com.mysql.jdbc.Driver和com.mysql.cj.jdbc.Driver的区别
    Java对元与分的金额的转换
  • 原文地址:https://www.cnblogs.com/wdt1/p/13599872.html
Copyright © 2020-2023  润新知