洛谷 P2015 二叉苹果树(树形DP)

题目链接:https://www.luogu.com.cn/problem/P2015

树上的0-1背包问题。

因为这是一棵树,所以当某条边被保留下来时,从根节点到这条边的路径上的所有边也都必须保留下来。

设dp[u][j]表示以u为根的子树中,选取j条边的最大值。

那么转移方程为:$dp[u][j]=max(dp[u][j],dp[u][j-1-k]+dp[v][k]+edge[i].val)$

u为当前节点,v是u的一个子节点,sum[u]表示u的子树上的边数,m为最多保留边数。

注意一些边界:

如为何是dp[u][j-1-k]而不是dp[u][j-k]或者为何k的取值范围要小于等于j-1而不是j:

因为这个子树与u之间有一条边,即u与v之间有一条边,需要-1。

AC代码:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 const int N=200;
 6 struct node{
 7     int to,next,val;
 8 }edge[N<<1];
 9 int head[N],tot;
10 int sum[N],dp[N][N],n,m;
11 void add(int u,int v,int w){
12     edge[tot].to=v;
13     edge[tot].next=head[u];
14     edge[tot].val=w;
15     head[u]=tot++;
16 }
17 void DFS(int u,int fa){
18     for(int i=head[u];i!=-1;i=edge[i].next){
19         int v=edge[i].to;
20         if(v==fa) continue;
21         DFS(v,u);
22         sum[u]+=sum[v]+1;//子树边数
23         for(int j=min(m,sum[u]);j>=1;j--){//注意边界
24             for(int k=min(j-1,sum[v]);k>=0;k--){//注意边界
25                 dp[u][j]=max(dp[u][j],dp[u][j-1-k]+dp[v][k]+edge[i].val);
26             }
27         }
28     }
29 }
30 int main(){
31     memset(head,-1,sizeof(head));
32     scanf("%d%d",&n,&m);
33     for(int i=1;i<n;i++){
34         int u,v,w;
35         scanf("%d%d%d",&u,&v,&w);
36         add(u,v,w); add(v,u,w);
37     }
38     DFS(1,-1);
39     printf("%d\n",dp[1][m]);
40     return 0;
41 }
AC代码

猜你喜欢

转载自www.cnblogs.com/New-ljx/p/12591649.html