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; } }