问题描述
有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了,那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?
输入格式
第一行包含一个整数 n 。
接下来的一行包含 n 个正整数,第 i 个正整数代表点 i 的权值。
接下来一共 n-1 行,每行描述树上的一条边。
输出格式
输出一个整数,代表选出的点的权值和的最大值。
样例输入
5
1 2 3 4 5
1 2
1 3
2 4
2 5
1 2 3 4 5
1 2
1 3
2 4
2 5
样例输出
12
样例说明
选择3、4、5号点,权值和为 3+4+5 = 12 。
数据规模与约定
对于20%的数据, n <= 20。
对于50%的数据, n <= 1000。
对于100%的数据, n <= 100000。
权值均为不超过1000的正整数。
#include<stdio.h>
#include<string.h>
int dp[100010][2], stack[100010], vis[100010], n;
struct point
{
int num, cnt, next[100];
} p[100010];
int Max(int x, int y)
{
return x>y?x:y;
}
int main()
{
int i, j, k;
while(scanf("%d", &n) != EOF)
{
memset(vis, 0, sizeof(vis));
for(i = 1; i <= n; ++i)
{
scanf("%d", &p[i].num);
p[i].cnt = 0;
}
for(i = 1; i < n; ++i)
{
scanf("%d%d", &j, &k);
p[j].next[p[j].cnt] = k;
p[j].cnt++;
p[k].next[p[k].cnt] = j;
p[k].cnt++;
}
int l = 0;
stack[l++] = 1;
vis[1] = 1;
k = 0;
while(l != n)
{
int temp = stack[k++];
for(i = 0; i < p[temp].cnt; ++i)
if(!vis[p[temp].next[i]])
{
stack[l++] = p[temp].next[i];
vis[p[temp].next[i]] = 1;
}
}
memset(dp, 0, sizeof(dp));
for(i = n-1; i >= 0; -- i)
{
int temp = stack[i];
if(p[temp].cnt == 1 && temp != 1)
{
dp[temp][0] = 0;
dp[temp][1] = p[temp].num;
}
else
{
for(j = 0; j < p[temp].cnt; ++j)
{
dp[temp][0] += Max(dp[p[temp].next[j]][0], dp[p[temp].next[j]][1]);
dp[temp][1] += dp[p[temp].next[j]][0];
}
dp[temp][1] += p[temp].num;
}
}
printf("%d\n", Max(dp[1][0], dp[1][1]));
}
return 0;
}
思路:给出一个DP数组记录以每个结点为根结点,选择该结点和不选择该结点所能获得的最大的权值和。
转移方程:
dp[temp][0] += Max(dp[p[temp].next[j]][0], dp[p[temp].next[j]][1]); 不选择该结点 则权值和为其所有子结点的最大权值和
dp[temp][1] += dp[p[temp].next[j]][0]; 选择该结点 则权值和只能是不选择子结点的所有权值和
注意:如果提交运行错误,八成是数组开太大了。