[关闭]
@Acqua 2019-01-23T23:51:43.000000Z 字数 1984 阅读 869

CF802K Send the Fool Further!(Jan. 22th, 2019) 树形DP

动态规划 难题

题目来源

题意

给定一棵有个结点的树,从点出发,每个点限制经过次,每条边有边权,求能够得到的最大权值和。

思路

表示遍历以为根的子树后是()否()回到点之上。

限制值实际上是限制取得的数量。

把当前遍历到的装到数组(双元,大小为)里排序。

  1. ,说明可以无限制取,故

  2. ,记前大的之和为
      等于前大的之和,因为回到点之上需要再经过一次点,所以必须少选一个子结点。
      对于而言,必须要有一个不回到点之上,所以通过枚举子结点来更新
      若子结点是前大的,直接用替换,即

      若子结点非前大,则用替换第大的(贪心分析),即
      此处,故该方程实质上为

反思

  1. 理清思路,攻坚战术,步步为营,问题推导。

代码

  1. // La Pluie
  2. #include<queue>
  3. #include<vector>
  4. #include<cstdio>
  5. #include<cstring>
  6. #include<iostream>
  7. #include<algorithm>
  8. using namespace std;
  9. const int N=1e5+5;
  10. typedef long long ln;
  11. vector <ln> e[N], w[N];
  12. pair<ln,ln> q[N];
  13. ln n,k,dp[N][2];
  14. void dfs(int u,int f){
  15. int t=0,l=e[u].size();
  16. for(int i=0;i<l;i++)
  17. if(e[u][i]!=f) dfs(e[u][i],u);
  18. for(int i=0;i<l;i++){
  19. if(e[u][i]!=f){
  20. int v=e[u][i];
  21. q[++t]=make_pair(dp[v][1]+w[u][i],v);
  22. }
  23. }
  24. sort(q+1,q+t+1);
  25. reverse(q+1,q+t+1);
  26. if(t<k){
  27. ln mx=0;
  28. for(int i=1;i<=t;i++){
  29. int v=q[i].second;
  30. dp[u][1]+=q[i].first;
  31. mx=max(mx,dp[v][0]-dp[v][1]);
  32. }
  33. dp[u][0]=dp[u][1]+mx;
  34. }
  35. else{
  36. ln sum=0;
  37. for(int i=1;i<=k;i++){
  38. if(i<k) dp[u][1]+=q[i].first;
  39. sum+=q[i].first;
  40. }
  41. for(int i=1;i<=t;i++){
  42. int v=q[i].second;
  43. if(i<=k) dp[u][0]=max(dp[u][0],sum+dp[v][0]-dp[v][1]);
  44. else dp[u][0]=max(dp[u][0],sum+(dp[v][0]+(q[i].first-dp[v][1]))-q[k].first);
  45. }
  46. }
  47. }
  48. int main(){
  49. scanf("%lld%lld",&n,&k);
  50. for(int i=1;i<n;i++){
  51. ln u,v,W;
  52. scanf("%lld%lld%lld",&u,&v,&W);
  53. u++,v++;
  54. e[u].push_back(v);
  55. e[v].push_back(u);
  56. w[v].push_back(W);
  57. w[u].push_back(W);
  58. }
  59. dfs(1,0);
  60. printf("%lld",max(dp[1][0],dp[1][1]));
  61. return 0;
  62. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注