【HAOI2015】 T1

为什么感觉越来越迷了X. X

原题:

有一棵点数为 N 的树,树边有边权。给你一个在 0~N 之内的正整数 K,你要在这棵树中选择 K 个点,将其染成黑色,并将其他的N-K 个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益。问受益最大值是多少。

n<=2000

 

一眼树形DP,然而状态似乎不太好转移啊

我最开始是f[i][j]表示第i个点为根的子树中j个黑点到i的距离和,然后发现还有白点的距离和要计算,再开一个数组又可能出现两种方案不一样的情况

思考无果,只能膜拜别人的题解,最后看到萌帝的

正解是f[i][j]表示第i个点为根的子树中有j个黑点的最优答案,转移的时候因为不论是白点还是黑点想要和这个子树外的白点/黑点连接一定会经过i和某子节点之间的边,所以"(外面黑点个数*里面黑点个数+外面白点个数*里面白点个数)*这条边的权值"就是对答案的贡献

然后枚举子树中有k个黑点,子树选k个黑点的最优答案+这条边对答案的贡献就是以i为根的子树中的最优答案,酱紫就可以往上转移了

还有一个问题就是f不能在一开始就初始化成0,而是要初始化成-oo并在每次dfs开始的时候f[x][0]=f[x][1]=0,因为这是一个类似背包的东西,而且必须保证这个包装满

感觉好迷啊,关于这题的具体思路和上面提到的背包的问题如何往更广泛的应用去扩展感觉很困难啊

还要再多想/看

(也许是我状态不好X. X

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 #define ll long long
 8 ll rd(){ll z=0,mk=1;  char ch=getchar();
 9     while(ch<'0'||ch>'9'){if(ch=='-')mk=-1;  ch=getchar();}
10     while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0';  ch=getchar();}
11     return z*mk;
12 }
13 struct ddd{int nxt,y;  ll v;}e[4100];  int lk[2100],ltp=0;
14 inline void ist(int x,int y,ll z){  e[++ltp].nxt=lk[x],lk[x]=ltp,e[ltp].y=y,e[ltp].v=z;}
15 int n,m;
16 ll f[2100][2100],sz[2100];
17 void dfs(int x,int y){
18     sz[x]=1,f[x][0]=f[x][1]=0;
19     ll bwl=0;
20     for(int i=lk[x];i;i=e[i].nxt)if(e[i].y!=y){
21         dfs(e[i].y,x);
22         sz[x]+=sz[e[i].y];
23         for(int j=sz[x];j>=0;--j)
24             for(int k=0;k<=sz[e[i].y] && k<=j;++k){
25                  bwl=(ll)k*(m-k)+(ll)(sz[e[i].y]-k)*(n-m-sz[e[i].y]+k);
26                  bwl=bwl*e[i].v+f[e[i].y][k];
27                  f[x][j]=max(f[x][j],f[x][j-k]+bwl);
28             }
29     }
30 }
31 int main(){//freopen("ddd.in","r",stdin);
32     memset(f,-10,sizeof(f));
33     cin>>n>>m;
34     ll l,r,v;
35     for(int i=1;i<n;++i){
36         l=rd(),r=rd(),v=rd();
37         ist(l,r,v),ist(r,l,v);
38     }
39     dfs(1,0);
40     cout<<f[1][m]<<endl;
41     return 0;
42 }
View Code

 

转载于:https://www.cnblogs.com/JSL2018/p/6472193.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值