• 年会 树状 DP


    Description

    内蒙古科技大学校长准备开一次年会。学校的员工和学生具有等级结构,即师生关系构成一棵树,以校长为树根,员工号是1到N之间的整数。人事部门把所有员工按活跃度排序,为了让年会使所有参加者都玩的高兴,校长不想让任何一个员工和他/她的直接导师同时被邀请。

    你的任务是列一张客人名单,以使客人活跃度最大。

    Input

    第1行是一个整数N(1 < = N < = 6000)。

    接下来N行包含相应员工的活跃度。活跃度是一个-128到127之间的整数。

    其后是师生关系表,每行有如下形式:

    L K

    表示第K个员工是第L个的直接导师。

    输入以:“0 0”(不含引号)结束。

    Output

    输出客人最大总活跃度。

    Sample Input

    7
    1
    1
    1
    1
    1
    1
    1
    1 3
    2 3
    6 4
    7 4
    4 5
    3 5
    0 0

    Sample Output

    5

    其实可以算是搜索吧,中间结果用数组存起来避免重复的递归调用。

    dp[root][k] 表示 root 结点能放、不能放时,root 这棵树能取到的最大值。(k = 0 时表示可以放 root 结点, k = 1 时表示不能放 root 结点)

    dp[root][0] = max(sum(dp[k][1]) + v[root], sum(dp[k][0]))    //可以放 root 的话,最大值就是放与不放的最大值

    dp[root][1] = max(sum(dp[k][0]))              //不可以放的话,就是子树加起来的最大值,此时对于子树来说,可以放子树的根结点

    解释下上面的状态转移方程,k 表示 root 的子树,sum表示求和,v[root] 表示 root 这个结点的值。

    sum(dp[k][1]) + v[root]  表示放 root 结点的情况。

    sum(dp[k][0]) 表示不放 root 结点的情况。

    代码:

    #include <iostream>
    #include <cstring>
    #include <vector> 
    using namespace std;
    
    const int MAX = 6005;
    
    int n;
    int v[MAX];
    bool isRoot[MAX];
    vector<int> tree[MAX];
    int dp[MAX][2];
    
    int dfs(int root, int k);
    
    int main(){
    //    freopen("input.txt", "r", stdin);
        
        cin >> n;
        memset(isRoot, true, sizeof(isRoot));
        for(int i=1; i<=n; i++){
            cin >> v[i];
        }
        
        int l, k;
        while(cin >> l >> k && l + k != 0){
            tree[k].push_back(l);
            isRoot[l] = false;
        }
        
        int ans = 0;
        memset(dp, -1, sizeof(dp));
        for(int i=1; i<=n; i++){
            if(isRoot[i]){
                ans += dfs(i, 0);
            }
        }
        cout << ans << endl;
        
        return 0;
    }
    
    
    
    int dfs(int root, int k){
        if(tree[root].size() == 0){
            if(k == 0 && v[root] > 0)
                return v[root];
            return 0;        //不能放或放了也是负的不如不放 
        }
        
        //如果 k == 1 即父亲放了,那么 root 不能放(父亲结点放没放,都可以选择 root 不放) 
        int sum1 = 0, sum2 = 0;
        if(k == 1 && dp[root][0] != -1)
            return dp[root][0];
        if(k == 0 && dp[root][1] != -1)
            return dp[root][1];
            
        for(int i=0; i<tree[root].size(); i++){
            int v = tree[root][i];
            sum1 += dfs(v, 0);        // root 没有放
        }
        //如果 k == 0 即父亲没有放,那么 root 可放可不放 
        if(k == 0){
            //如果放的话 
            for(int i=0; i<tree[root].size(); i++){
                int v = tree[root][i];
                sum2 += dfs(v, 1);        // root 放了 
            }
            dp[root][1] = max(sum1, sum2 + v[root]);
            return dp[root][1];
        }else{
            dp[root][0] = sum1;
            return sum1;
        }
    }
  • 相关阅读:
    【转】请说出三种减少页面加载时间的方法。
    【转】Web前端性能优化——如何提高页面加载速度
    【转】数据分析sql常用整理
    【转】消息中间件系列之简单介绍
    Could not load file or assembly 'System.Core, Version=2.0.5.0 和autofac冲突的问题
    云主机与传统主机性能对比表
    真假云主机,VPS资料集合
    将网站部署到windows2003 iis6之后,出现asp.net程序页面无法访问情况
    想当然是编程最大的坑,记更新删除过期cookie无效有感
    FlashBuilder(FB/eclipse) 打开多个无效
  • 原文地址:https://www.cnblogs.com/lighter-blog/p/7308520.html
Copyright © 2020-2023  润新知