年会 树状 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;
    }
}

 

posted @ 2017-08-08 19:16  淡蓝色光  阅读(198)  评论(0编辑  收藏  举报