★★☆ 输入文件:haoi2015_t1.in
输出文件:haoi2015_t1.out
简单对比 时间限制:1 s 内存限制:256 MB
【题目描述】
有一棵点数为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
题解:
这是一道树形DP,考虑对于每一条边,它对答案的贡献值=两端的黑点个数乘积*边权+两端白点个数乘积*边权。
令f[i][j]表示以i为根的子树中,有j个黑点的最大收益。对于某一个节点x及其某一儿子y,考虑x与y的连边对答案的贡献,我们可以先枚举x中的黑点个数,再枚举y的黑点个数,用类似01背包来转移。
1 /************************************************************** 2 Problem: 4033 3 User: __abcdef__ 4 Language: C++ 5 Result: Accepted 6 Time:6824 ms 7 Memory:32932 kb 8 ****************************************************************/ 9 10 #include11 #include 12 #include 13 #include 14 #include 15 #include 16 #include 17 #include 18 using namespace std;19 typedef long long LL;20 const LL inf=1e15,maxn=2010;21 LL N,K;22 vector to[maxn],cost[maxn];23 LL fa[maxn],f[maxn][maxn],siz[maxn];24 inline void dfs(LL x,LL fath){25 fa[x]=fath; siz[x]=1;26 for(int i=0;i =0;tot--){ //枚举以x为根的子树中有几个黑点 43 for(int j=0;j<=min(siz[y],K)&&j<=tot;j++){ //这个子树中有多少黑点 44 LL ans1=(LL)j*(K-(LL)j)*val;45 LL ans2=(siz[y]-(LL)j)*(N-K-(siz[y]-(LL)j))*val;46 LL tmp=f[y][j]+ans1+ans2;47 f[x][tot]=max(f[x][tot],f[x][tot-j]+tmp); 48 }49 }50 }51 }52 }53 54 int main(){55 scanf("%lld%lld",&N,&K);56 for(int i=1;i<=N-1;i++){57 LL u,v,c;58 scanf("%lld%lld%lld",&u,&v,&c);59 to[u].push_back(v); cost[u].push_back(c);60 to[v].push_back(u); cost[v].push_back(c);61 }62 for(int i=1;i<=N;i++){63 for(int j=1;j<=N;j++){64 f[i][j]=-inf;65 }66 }67 dfs(1,-1);68 calc(1);69 printf("%lld\n",f[1][K]);70 return 0;71 }