题目:
时间限制:1.0s 内存限制:256.0MB
问题描述
有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了,那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?
输入格式
第一行包含一个整数 n 。
接下来的一行包含 n 个正整数,第 i 个正整数代表点 i 的权值。
接下来一共 n-1 行,每行描述树上的一条边。
输出格式
输出一个整数,代表选出的点的权值和的最大值。
样例输入
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的正整数。
分析:
归类为树形dp, 找一个只有三个节点的情况仔细分析一下最容易获得状态转移方程:
-
叶子节点
-
非叶子顶点 , 其从做到右第 个孩子结点为 , 其权重为
-
最终结果为(为选择的树的根节点)
关键点:
- 怎么存储这个树?
因为给出两个点但是没有说谁是父亲谁是孩子, 因此使用邻接表存储一个无向图的方式来表示。
- 超时问题:
我第一次使用C++提供的STL来常见邻接表, 直接50%的超时。
map<int, vector<int> >;
后修改为如下结构的方式超时问题解决:
int m = 0;
// 表示一个树结点的链表结点, 使用静态数组的方式加快速度
struct edge
{
int v, int next;
}edges[200001];
head[100001]; // memset(head, -1, sizeof(head))
void addEdge(int from, int to){
edge[m].v = to;
edge[m].next = head[from];
head[from] = m++;
edge[m].v = from;
edge[m].next = head[to];
head[to] = m++;
};
- 如何解决无向图表示的树的后序遍历的无限递归问题
在DFS函数当中添加一个pre参数, 表明其父亲节点即可, 当孩子不等于父亲则可以DFS。
代码:
#include <bits/stdc++.h>
using namespace std;
int N, t, b, e;
int weights[100001];
int head[100001]; /* 邻接表头*/
int dp[100001][2];
int m = 0; /* 指针*/
struct edge{ /* 邻接表使用的边节点*/
int v, next;
}edges[200001];
void addEdge(int from, int to)
{
// 以to为终点的边, 头查的方式插入
edges[m].v = to;
edges[m].next = head[from];
head[from] = m++;
// 以from为终点的边
edges[m].v = from;
edges[m].next = head[to];
head[to] = m++;
}
void DFS(int root, int pre)
{
dp[root][0] = 0;
dp[root][1] = weights[root];
// 遍历链表
// cout << root << endl;
for(int n = head[root]; n != -1; n = edges[n].next){
int v = edges[n].v;
// if(root == 1) cout << v <<endl;
if(v == pre) continue;
DFS(v, root); // 后序遍历
dp[root][0] += max(dp[v][0], dp[v][1]);
dp[root][1] += dp[v][0];
}
//printf("dp[%d][0] = %d -- dp[%d][1] = %d\n", root, dp[root][0], root ,dp[root][1]);
}
int main()
{
#ifdef LOCAL
freopen("ALGO-4.in", "r", stdin);
#endif
std::ios::sync_with_stdio(false);
memset(head, -1, sizeof(head));
int ans = 0;
cin >> N;
for(int i = 1; i <= N; ++i)
cin >> t, weights[i] = t;
while(cin >> b >> e)
addEdge(b, e);
DFS(1, -1);
cout << max(dp[1][0], dp[1][1]) << endl;
return 0;
}