题目大意:一颗有根树上每个根结点有一个权值,相邻的父结点和子结点只能选择一个,问如何选择,使得总权值之和最大。(邀请员工参加宴会,为了避免员工和直属上司发生尴尬,规定员工和直属上司不能同时出席。)
输入:结点编号从1到N。输入的第一行包含数字N。1<= N <= 6000。随后的N行中的每一行都包含结点的权值。范围是一个介于-128到127之间的整数。下面是T行,描述一个父子关系,每一行都有如下形式:
L K
第K个结点是第L个结点的父结点。读到0 0时结束。
输出:输出总的最大权值。
输入样例:
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0
输出样例:
5
解题代码
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 6000 + 10; //dp[i][0]表示不选择当前结点i时的最优解
int value[maxn], dp[maxn][2], father[maxn], n; //dp[i][1]表示选择当前结点i时的最优解
vector<int> tree[maxn]; //用邻接表建树
void dfs(int u) {
dp[u][0] = 0; //赋初值:不参加宴会
dp[u][1] = value[u]; //赋初值:参加宴会
for(int i = 0; i < tree[u].size(); i++) { //逐一处理这个父结点的每个子结点
int son = tree[u][i];
dfs(son); //深搜子结点
dp[u][0] += max(dp[son][1], dp[son][0]); //父结点不选,子结点可选可不选,并继承两者中的最大值
dp[u][1] += dp[son][0]; //父结点选择,子结点不能选择
}
}
int main()
{
while (~scanf("%d",&n)) { //当读完时,scanf返回EOF(即-1,然后-1的二进制码各个位上全是1),按位取反后-1会变成0
for(int i = 1; i <= n; i++) {
scanf("%d",&value[i]);
tree[i].clear(); //赋初值,还没建立关系
father[i] = -1;
}
while (1) {
int t1, t2;
scanf("%d %d",&t1,&t2);
if (t1 == 0 && t2 == 0) break;
tree[t2].push_back(t1); //用邻接表建树
father[t1] = t2;
}
int root = 1;
while (father[root] != -1) { //查找树的根结点
root = father[root];
}
dfs(root); //从根结点开始,用DFS遍历整棵树
printf("%d\n", max(dp[root][1], dp[root][0]));
}
return 0;
}