ZOJ3201 Tree of Tree
Tree of Tree
You're given a tree with weights of each node, you need to find the maximum subtree of specified size of this tree.
Tree Definition
A tree is a connected graph which contains no cycles.
Input
There are several test cases in the input.
The first line of each case are two integers N(1 <= N <= 100), K(1 <= K <= N), where N is the number of nodes of this tree, and K is the subtree's size, followed by a line with N nonnegative integers, where the k-th integer indicates the weight of k-th node. The following N - 1 lines describe the tree, each line are two integers which means there is an edge between these two nodes. All indices above are zero-base and it is guaranteed that the description of the tree is correct.
Output
One line with a single integer for each case, which is the total weights of the maximum subtree.
Sample Input
3 1 10 20 30 0 1 0 2 3 2 10 20 30 0 1 0 2
Sample Output
30 40
月赛难得和long1兄合作,十分愉快。虽然身在Turkey,但是long1兄不忘参加月赛提高自己,令我十分敬佩。
当时long1兄匪夷所思地写出这道树型DP,我不是很理解,特此研究代码,总结下。
// 树型DP,DFS
// 思路:
// F[T][k]表示以T为根节点,大小为k的树所能得到的最大权和
// 从任意一点开始做DFS,确立节点的双亲-孩子关系
// DFS完一颗子树后,更新其双亲节点的DP值
// 更新过程为: (假设当前双亲点为T,刚遍历完子树S)
// 枚举T树的大小k,枚举T树上的点不落在S树上的个数k',那么将有k-k'个点落在S树上
// F[T][k] = max{F[T][k']+F[S][k-k'] | 1<=k'<=k} (可以是k-1)
// 这里要注意下,如果已经DFS过的子树总共只有n个点,那么F[T]最多只能更新到F[T][n+1]
// 所有子树DFS完毕后,F[T]的值才算计算完毕.
// 一开始想,为什么F[T][k]的计算过程中,不考虑T的祖先节点T',因为T'也可以看成是T的孩子
// 其实在DFS回归的过程中,会计算T'的DP值,而T是T'的子树.所以上述情况已经在计算T'的过程中被考虑进去了
// 时间复杂度O(n^3)
#include <iostream>
#include <vector>
using namespace std;
int g_nNode, g_k;
int w[100]; // 节点权值
vector<int> conn[100]; // 邻接链表
int F[100][101]; // DP数组
inline int mmax(int a, int b) {
return a>b?a:b;
}
void DP(int node, int child) {
int sz;
int k;
for(sz=g_k; sz>=1; sz--) {
// 以node为根的sz大小的子树
for(k=sz; k>=1; k--) {
// 其中k个节点在node上或者之前遍历过的node的子树上, sz-k个节点在child树上
// F[node][sz]均从F[node][sz-i]求得,所以这里按照sz递减的顺序计算以免新DP值覆盖前一次DP值
F[node][sz] = mmax(F[node][sz], F[node][k] + F[child][sz-k]);
}
}
}
void DFS(int node, int parent) {
F[node][1] = w[node];
int child;
for(int i=0; i<(int)conn[node].size(); i++) {
child = conn[node][i];
if( child == parent )
continue;
DFS(child, node); // DFS子树
DP(node, child); // 子树DFS完毕后更新当前点的DP值
}
}
int main() {
while( scanf("%d %d", &g_nNode, &g_k) != EOF ) {
// 输入,以及建立邻接矩阵
int i;
for(i=0; i<g_nNode; i++) {
scanf("%d", w+i);
conn[i].resize(0);
}
int from, to;
for(i=0; i<g_nNode-1; i++) {
scanf("%d %d", &from, &to);
conn[from].push_back(to);
conn[to].push_back(from);
}
memset(F, 0, sizeof(F));
DFS(0, 0);
// 取以任意点为根,大小为k的树中权最大的一个
int res = 0;
for(i=0; i<g_nNode; i++) {
res = mmax(res, F[i][g_k]);
}
printf("%d\n", res);
}
return 0;
}