题目描述
有一棵点数为 (N) 的树,树边有边权。
给你一个在 (0 sim N) 之内的正整数 (K),你要在这棵树中选择 (K) 个点,将其染成黑色,并将其他的 (N-K) 个点染成白色。
将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。
问收益最大值是多少!
输入格式
第一行两个整数 (N,K)。
接下来 (N-1) 行每行三个正整数 (fr,to,dis),表示该树中存在一条长度为 (dis) 的边 ((fr,to))。
输入保证所有点之间是联通的。
输出格式
输出一个正整数,表示收益的最大值。
数据范围
(1 le N le 2000),
(0 le K le N)
输入样例:
5 2
1 2 3
1 5 1
2 3 1
2 4 2
输出样例:
17
样例解释
将点 (1,2) 染黑就能获得最大收益。
解题报告
题意理解
原题很清晰,没法简析了。
探索算法
有一说一,第一眼看到这道题目,博主是真的没有认为这道题目的是一个树形DP。
但是,染色的题目,加上最值问题的要求,还是和树形DP联系很大。
那么我们为什么很难看出来呢?可能是因为我太菜了
因为我们的关注点,是一个点,它所提供的贡献。
而不是一条边,所提供的贡献
其实这也是一个难想&常见的思路。
对于树上的题目,求贡献,不是点,就是边。至少提高组范畴是这样的
那么如果说,我们按照边来思考的话,这道题目的转移方程还是很有意思的。
算法解析
类似于状态压缩动态规划,树形DP的题目,状态设计是极为重要的一步。
那么如何设计状态呢?
既然上面,我们说了,要按照边来算贡献,那么我们就先来分析边,是如何提供贡献的?
对于一条边而言,他可以提供的贡献,来自于两个节点之间的路径有它,也就是经过它:
- 左右两边黑色节点提供的。(自己子树内的黑色节点 $ imes $ 子树外黑色节点。
- 左右两边白色节点提供的。(自己子树内的白色节点 $ imes $ 子树外白色节点。
那么此时我们需要确定状态了
( f[x][i] 表示节点x它到父亲节点的这条边,他的子树有i个染黑节点,此时提供的最大贡献 )
那么对于一个节点他有多少个染黑节点呢?
不知道?
那么枚举吧。
于是本题成了一个树形背包。
每个节点他的子树有多少个黑色节点(体积),然后算出这条边对答案的贡献(价值)
因此本题就这么愉快的解决了,在这里默认,树形背包DP大家都会吧。。。
如果不会的话,可以参照Acwing我曾经的背包DP的讲义。
参考代码
//My English is poor.The Code maybe have some grammer problems.
//To have a better Font display in Acwing.com,I must choose English.
#include <bits/stdc++.h>
using namespace std;
const int N=2100;
int n,k,head[N<<1],edge[N<<1],Next[N<<1],tot,Size[N];
long long f[N][N],ver[N<<1];
inline void add_edge(int a,int b,int c)
{
edge[++tot]=b;
ver[tot]=c;
Next[tot]=head[a];
head[a]=tot;
}
void tree_DP(int x,int fa)
{
Size[x]=1;
memset(f[x],-1,sizeof(f[x]));
f[x][0]=f[x][1]=0;
for(int i=head[x]; i; i=Next[i])//visit its sons
{
int y=edge[i];
if (y==fa)//its father
continue;//can't visit its father node
tree_DP(y,x);//visit the son
Size[x]+=Size[y];//count the Size of the tree
}
//Next part is the DP whose kind is knapsack.
for(int i=head[x]; i; i=Next[i])
{
int y=edge[i];
if (y==fa)//its father
continue;//can't visit its father node
for(int j=min(k,Size[x]); j>=0; j--)//choose black nodes whose number is j
{
for(int s=0; s<=min(j,Size[y]); s++)
if (~f[x][j-s])
{
long long black_sum=s*(k-s);//balck pairs
long long white_sum=(Size[y]-s)*((n-Size[y])-(k-s));//white pairs
f[x][j]=max(f[x][j],f[x][j-s]+f[y][s]+(black_sum+white_sum)*ver[i]);
}
}
}
}
inline void init()
{
scanf("%d%d",&n,&k);
for(int i=1; i<n; i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add_edge(a,b,c);
add_edge(b,a,c);
}
tree_DP(1,0);
printf("%lld
",f[1][k]);
}
signed main()
{
init();
return 0;
}