关于树的动态规划,题目如下:
有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了,那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?
第一行包含一个整数 n 。
接下来的一行包含 n 个正整数,第 i 个正整数代表点 i 的权值。
接下来一共 n-1 行,每行描述树上的一条边。
1 2 3 4 5
1 2
1 3
2 4
2 5
对于20%的数据, n <= 20。
对于50%的数据, n <= 1000。
对于100%的数据, n <= 100000。
权值均为不超过1000的正整数。
这里我并没立刻开始进行建树,因为总是觉得对于二叉树进行各种转换比较让我烦,所以就想是否可以利用数组来解决树的问题,但是用数组如何解决父子的这种关系呢? 很是头疼,因为本人是初学者, 所以就上网参考大神的代码,终于找到我想要的,但是没有注释,无语,所以边猜边想的看完了,想了一天,最后明白了原理了:
首先是既然是动态规划,必然少不了记录用的表格,但是这个怎么建呢?我感觉这也是数据结构的难点所在,就是你要创建一个十分适合你算法的数据结构,蛋疼啊, 既然对于每一个节点只有两种可能,即是选上或者是不选上,所以就建立一个二维数组,比如a[N][2], 这里N表示有多少个节点,而后面是2的原因因为每个节点就两种选择,所以a[1][0]表示第一个节点不选,而a[1][1]表示第一个节点选上,然后你就可以直接开始逆推了;
程序如下:
#include <iostream> #include <cstdio> #include <vector> using namespace std; vector<int> vec[100001]; int value[100001][2]; void dp(int u, int v) { for(int i=0; i<vec[u].size(); i++) { int k = vec[u][i]; if( k != v ) { dp(k, u); //这里并没有确定选还是不选择该节点,只不过将如果不选的话,然后就对当前的孩子的值加到到父亲的里面 //如果选的话, 就把孩子不选的值加到这里 value[u][1] += value[k][0]; value[u][0] += ( value[k][0] > value[k][1] ? value[k][0] : value[k][1] ); } } } int main() { int count; scanf("%d", &count); for(int i=1; i<=count; i++) { int val; scanf("%d", &val); value[i][1] = val; } for(int i=1; i<count; i++) { int s, e; scanf("%d %d", &s, &e); //这里就涉及到了他的树是如何通过数组进行建立的了 //每次输入父节点和子节点的时候,就分别向父数组中存入孩子的值 //向孩子数组存入父亲的值 这样就可以通过孩子的数组找到父亲了 vec[s].push_back(e); vec[e].push_back(s); } for(int i=0; i<count; i++) { //这里为什么要选择一个叶子节点进行运算呢? //原因是如果按正常的想法首先是从树的根进行网下递归 但是这个时候就要判断他的两个孩子 //是否被选上, 而以叶子节点开始呢?? 就不必判断两个点了 直接判断他的父亲节点选还是不选的时候 //值最大, 所以要从叶子节点开始 if( vec[i].size() == 1 ) { dp(i, -1); printf("%d\n", (value[i][0]>value[i][1] ? value[i][0] : value[i][1]) ); break; } } }