http://codeforces.com/contest/802/problem/K

【题意】

给定一棵树,Heidi从根结点0出发沿着边走,每个结点最多经过k次,求这棵树的最大花费是多少(同一条边走n次花费只算一次)

【思路】

对于结点v:

  • 如果在v的某棵子树停下,那么可以“遍历”k棵子树(有的话)
  • 如果还要沿着v返回v的父节点p,那么只能“遍历”k-1棵子树(有的话)。

用dp[v][1]表示第一种情况,dp[v][0]表示第二种情况;最后要求的就是dp[0][0]。

1. 对于dp[v][1],把所有的子树从大到小排序

【树形DP】codeforces K. Send the Fool Further! (medium)(t=k-1)

2. 对于dp[v][0],枚举子结点dp[u][0]中的u,剩下的k-1个dp[u][1]取最大的,所以我们可以这样预处理:

sum=

【树形DP】codeforces K. Send the Fool Further! (medium)(t=k)

  • 如果u<k,则target=sum-dp[u][1]+dp[u][0]
  • 否则,        target=sum-dp[t][1]+dp[u][0](t是从大到小排序后的第k-1个)

这样,dp[0][0]就是所求结果(dp[0][0]一定大于dp[0][1]),时间复杂度是O(nlogn)

【官方题解】

【树形DP】codeforces K. Send the Fool Further! (medium)

【树形DP】codeforces K. Send the Fool Further! (medium)

【Accepted】

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<string>
  5 #include<cmath>
  6 #include<vector>
  7 #include<algorithm>
  8 
  9 using namespace std;
 10 int n,m;
 11 vector< vector< pair<int,int> > > g;
 12 const int maxn=1e5+5;
 13 int dp[maxn][2];
 14 void dfs(int v,int p,int edge)
 15 {
 16     //从p到v的花费要算在v里
 17     dp[v][0]+=edge;
 18     dp[v][1]+=edge;
 19     vector< pair<int,int> > s;
 20     //只有根结点没有父节点,非根结点有父节点,减去1
 21     if(v==0)
 22     {
 23         s.resize(g[v].size());
 24     }
 25     else
 26     {
 27         s.resize(g[v].size()-1);
 28     }
 29     //遍历
 30     int num=0;
 31     for(int i=0;i<g[v].size();i++)
 32     {
 33         int to=g[v][i].first;
 34         if(to==p)
 35         {
 36             continue;
 37          }
 38         dfs(to,v,g[v][i].second);
 39         s[num++]={dp[to][1],to};
 40     }
 41     //从大到小排序
 42     sort(s.begin(),s.end());
 43     reverse(s.begin(),s.end());
 44     //要记录各个子结点的rank,后面dp[v][0]枚举u是要分类
 45     int pos[maxn];
 46     for(int i=0;i<s.size();i++)
 47     {
 48         pos[s[i].second]=i;
 49     }
 50     //计算dp[v][1]
 51     for(int i=0;i<min(m-1,(int)s.size());i++)
 52     {
 53         dp[v][1]+=s[i].first;
 54     }
 55     //计算dp[v][0]
 56     int sum=0;
 57     for(int i=0;i<min(m,(int)s.size());i++)
 58     {
 59         sum+=s[i].first;
 60     }
 61     int maxu=-1;
 62     //枚举
 63     for(int i=0;i<g[v].size();i++)
 64     {
 65         int to=g[v][i].first;
 66         if(to==p)
 67         {
 68             continue;
 69         }
 70         if(pos[to]<m)
 71         {
 72             maxu=max(maxu,sum-dp[to][1]+dp[to][0]);
 73         }
 74         else
 75         {
 76             maxu=max(maxu,sum-s[m-1].first+dp[to][0]);
 77         }
 78     }
 79     if(maxu>-1)
 80     {
 81         dp[v][0]+=maxu;
 82     }
 83 }
 84 int main()
 85 {
 86     while(~scanf("%d%d",&n,&m))
 87     {
 88         memset(dp,0,sizeof(dp));
 89         g.resize(n);
 90         int u,v,c;
 91         for(int i=0;i<n-1;i++)
 92         {
 93             scanf("%d%d%d",&u,&v,&c);
 94             g[u].push_back({v,c});
 95             g[v].push_back({u,c});
 96         }
 97         //根结点为0,无父结点,根结点到父结点的花费也为0
 98         dfs(0,0,0);
 99         printf("%d\n",dp[0][0]);
100     }
101     return 0;
102  }
View Code

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-01-19
  • 2021-12-30
  • 2021-10-09
猜你喜欢
  • 2021-11-17
  • 2022-01-01
  • 2021-06-27
  • 2022-12-23
  • 2021-09-17
  • 2022-02-12
  • 2022-12-23
相关资源
相似解决方案