【题目描述】
有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。问收益最大值是多少。
【输入格式】
第一行两个整数N,K。
接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to)。输入保证所有点之间是联通的。
【输出格式】
输出一个正整数,表示收益的最大值。
【输入样例1】
3 1
1 2 1
1 3 2
【输出样例1】
3
【输入样例2】
5 2
1 2 3
1 5 1
2 3 1
2 4 2
【输出样例2】
17
【样例解释】
在第二个样例中,将点1,2染黑就能获得最大收益。
【数据范围】
对于30%的数据,N<=20
对于50%的数据,N<=100
对于100%的数据,N<=2000,0<=K<=N。
题解:
其实这个题只需要考虑每个子树和他父亲的那条边对答案做了多少贡献即可(也就是子树内的白点数乘上子树外的白点数再加上子树内的黑点数乘上子树外的黑点数,最后再乘上边权),这个问题是可以分割成子问题的,对于每个子问题我们就可以枚举父节点给他的每个儿子分多少个黑点,然后就成了树形背包了。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
int n,kk,y,z,x;;
long long f[2010][2010],sz[2010];
vector<pair<int,int> > a[2010];
void dfs(int x,int fa)
{
f[x][0]=f[x][1]=0;
int u;
long long ans;
sz[x]=1;
for (int i=0;i<a[x].size();i++)
{
u=a[x][i].first;
if (u==fa) continue;
dfs(u,x);
sz[x]+=sz[u];
for(int j=sz[x];j>=0;j--)
{
for(int k=0;k<=sz[u]&&k<=j;k++)
{
ans=(long long)(k*(kk-k))+(long long)((sz[u]-k)*(n-kk-(sz[u]-k)));
ans*=a[x][i].second;
ans+=f[u][k];
f[x][j]=max(f[x][j],f[x][j-k]+ans);
}
}
}
}
int main()
{
freopen("haoi2015_t1.in","r",stdin);
freopen("haoi2015_t1.out","w",stdout);
scanf("%d%d",&n,&kk);
for (int i=1;i<=n-1;i++)
{
scanf("%d%d%d",&x,&y,&z);
a[x].push_back(make_pair(y,z));
a[y].push_back(make_pair(x,z));
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
f[i][j]=-999999999999;
dfs(1,0);
printf("%lld",f[1][kk]);
}
时间: 2024-10-11 11:17:36