洛谷$2015$二叉苹果树
Sol
首先需要根据题目条件把苹果树建出来
容易想到f[i][j]表示以i结点为根的苹果树上保留j个结点所能保留的最大苹果树
需要注意的是f[i][j]包括i结点(根)与它的父亲联结的枝条上的苹果
转移的话就枚举i的第一个子结点保留的结点数k,那么另一个子结点保留的就是j-k-1
这题的转移还是很简单的,因为它是二叉的,只有二叉鸭
如果是多叉呢? 那不就和选课一样了嘛,是一个树形背包问题 树形DP+分组背包
和选课略有不同的是苹果在边上,而课程在结点上
Code
(是很久以前的code,好像和现在风格不太一样QwQ,大概是因为以前不压行??)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 int dp[110][110]; 6 int a[110]; 7 int map[110][110]; 8 int lc[110],rc[110]; 9 int n,q; 10 void build(int now) 11 { 12 for(int i=1;i<=n;i++) 13 { 14 if(map[now][i]>=0) 15 { 16 lc[now]=i; 17 a[i]=map[now][i]; 18 map[now][i]=-1;map[i][now]=-1; 19 build(i); 20 break; 21 } 22 } 23 for(int i=1;i<=n;i++) 24 { 25 if(map[now][i]>=0) 26 { 27 rc[now]=i; 28 a[i]=map[now][i]; 29 map[now][i]=-1;map[i][now]=-1; 30 build(i); 31 break; 32 } 33 } 34 } 35 int DP(int now,int data) 36 { 37 if(dp[now][data]) return dp[now][data]; 38 if(data==0) return 0; 39 if(lc[now]==0&&rc[now]==0) return a[now]; 40 for(int i=0;i<=data-1;i++) 41 { 42 dp[now][data]=max(dp[now][data],DP(lc[now],i)+DP(rc[now],data-i-1)+a[now]); 43 } 44 return dp[now][data]; 45 } 46 int main() 47 { 48 cin>>n>>q;q++; 49 memset(map,-1,sizeof(map)); 50 for(int i=1;i<n;i++) 51 { 52 int x,y,z;cin>>x>>y>>z; 53 map[x][y]=map[y][x]=z; 54 } 55 build(1); 56 cout<<DP(1,q); 57 return 0; 58 }
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 int dp[110][110]; //dp[i][j] 以i为根节点的树上保留j个节点的最大苹果数 6 //注意dp[i][j]包括根节点和它的父亲的边上的苹果数 7 //保留i个节点包括根节点 也就是说它的孩子结点只能保留i-1个 8 int a[110]; //a[i]节点i与它的父亲相连的边上的苹果数 9 int map[110][110]; //map[i][j]:i与j连接的边上的苹果树 若无边则初始化为-1 10 int lc[110],rc[110]; //lc[i]:i的左孩子 rc[]:右孩子 11 int n,q; 12 void build(int now) //建树 13 { 14 for(int i=1;i<=n;i++) 15 { 16 if(map[now][i]>=0) //找到第一个与now相连的节点作为它的左孩子 17 { 18 lc[now]=i; 19 a[i]=map[now][i]; 20 map[now][i]=-1;map[i][now]=-1; //记录过了 21 build(i); 22 break; //找到一个就退出循环 进入下一个循环寻找右孩子 23 } 24 } 25 for(int i=1;i<=n;i++) 26 { 27 if(map[now][i]>=0) 28 { 29 rc[now]=i; 30 a[i]=map[now][i]; 31 map[now][i]=-1;map[i][now]=-1; 32 build(i); 33 break; 34 } 35 } 36 } 37 int DP(int now,int data) //以now为根节点的树上保留data个结点 38 { 39 if(dp[now][data]) return dp[now][data]; //搜过了 40 if(data==0) return 0; 41 if(lc[now]==0&&rc[now]==0) return a[now]; 42 for(int i=0;i<=data-1;i++) 43 { 44 dp[now][data]=max(dp[now][data],DP(lc[now],i)+DP(rc[now],data-i-1)+a[now]); 45 } 46 return dp[now][data]; 47 } 48 int main() 49 { 50 cin>>n>>q;q++; //q++!!! 因为输入的q是保留q条边 即为保留q+1个结点 51 memset(map,-1,sizeof(map)); //初始化 52 for(int i=1;i<n;i++) 53 { 54 int x,y,z;cin>>x>>y>>z; 55 map[x][y]=map[y][x]=z; //先邻接矩阵储存 56 } 57 build(1); //以1为根结点建树 其实就是为了记录每个点的左右孩子以及它与自己父亲相连的边上的结点数 58 cout<<DP(1,q); //树形DP 59 return 0; 60 }
光伴随的阴影