题目
有N个小球,上面有数字,分别对应1……N,每个小球有个价值,第i个小球价值对应Value[i];有N-1个木棍,一个木棍两端分别连接着一个小球,把N个小球连接起来,并且保证任意两个小球间都不存在两条不同的路径可以互相到达。
现在要把1号小球连通的M个刷上油漆(连通指的是这一些涂漆的小球可以互相到达并且不会经过没有涂漆的小球),要求使这M个小球的值得和最大。
输入
每个测试点(输入文件)有且仅有一组测试数据。
每组测试数据的第一行为两个整数N、M,意义如前文所述。
每组测试数据的第二行为N个整数,其中第i个整数Vi表示标号为i的结点的评分
每组测试数据的第3~N+1行,每行分别描述一根木棍,其中第i+1行为两个整数Ai,Bi,表示第i根木棍连接的两个小球的编号。
对于100%的数据,满足N<=10^2,1<=Ai<=N, 1<=Bi<=N, 1<=Vi<=10^3, 1<=M<=N
测试样例
样例输入
10 4
370 328 750 930 604 732 159 167 945 210
1 2
2 3
1 4
1 5
4 6
4 7
4 8
6 9
5 10
样例输出
2977
解法
f(t, m)表示,在以t为根的一棵树中,选出包含根节点t的m个连通的结点,能够获得的最高的评分,然后我们的答案就是f(1, M)。
首先我要包含根节点,然后与根节点连通的结点最开始便是根节点的子结点,而所有选择的结点都要互相连通的话,那么如果选择某一棵子树中的结点的话就势必也需要选择这棵子树的根节点——所以就变成了一个规模小一些的子问题。比如在求解f(t, m)的时候,我先枚举t的第一个子结点t1中选出的结点数m1,然后枚举t的第二个子结点t2中选出的结点数m2……一直到t的最后一个子结点tk中选出的结点数mk,这样就有f(t, m) = max{f(t1, m1) + f(t2, m2) + …… + f(tk, mk)} + v(t),并且需要保证m1+m2+…+mk+1=m。
这样算法复杂度太高,m1…mk可能有的方案数可是非常多。
这个问题有点像无限背包问题了,我可以不用单独的求解每一个f(t, m)而是针对于每一个t,同时求解它的f(t, 0..M),这样的话,我就可以把m视作背包容量,把每个子结点t_child都视作一件单位重量为1的物品,但是和背包问题不同的是,这件物品的总价值并不是单位价值乘以总重量,而是重量为m_child的该物品的价值为f(t_child, m_child),这样我就可以像无限背包问题一样,用这样的方法来进行求解!
对任意一个节点t,计算f[t][0……M]
set f[t][0……M]=0;
f[t][1]=v[t];
f[t][0]=0;
for t_child=t的每个孩子结点
for m=M……2
for m_child=1……m-1
f[t][m]=max(f[t][m],f[t][m-m_child]+f[t_child][m_child];
写代码测试
#include<iostream>
#include<vector>
#include<algorithm>
std::vector<std::vector<int> > G(101);
int Value[101];
int f[101][101];
///pre上一个访问结点;current当前访问结点;M:current为根的树种,结点个数
void Visit(int pre, int current, int M)
{
if (M == 1)
return;
for (int i = 0; i < G[current].size(); ++i)
{
if (G[current][i] == pre)
continue;
Visit(current, G[current][i], M - 1);
}
for (int i = 0; i < G[current].size(); ++i)
{
if (G[current][i] == pre)
continue;
for (int m = M; m >= 2; --m)
{
for (int m_child = 1; m_child < m; ++m_child)
{
f[current][m] = std::max(f[current][m], f[current][m - m_child] + f[G[current][i]][m_child]);
}
}
}
}
int main()
{
int N, M;
std::cin >> N >> M;
for (int i = 1; i <= N; ++i)
{
std::cin >> Value[i];
f[i][1] = Value[i];
f[i][0] = 0;
}
int Ai, Bi;
for (int i = 1; i < N; ++i)
{
std::cin >> Ai >> Bi;
G[Ai].push_back(Bi);
G[Bi].push_back(Ai);
}
Visit(1, 1, M);
std::cout << f[1][M] << std::endl;
}