这道题的状态转移方程极其好想,不会可以回家洗洗睡了
dp[now][j]=max(dp[now][j],dp[now][j-k-1]+dp[to][k]+edge[i].val)
但是!!
调试极其毒瘤!
本以为背包背的是边和点差不多,结果发现恶心至极
1 int DP(int now,int fa) 2 { 3 int sum=0; 4 for(int i=head[now],to;i!=-1;i=edge[i].nxt) 5 { 6 to=edge[i].to; 7 if(to==fa)continue; 8 leaf=0; 9 int tt=DP(to,now); 10 sum+=tt; 11 for(int j=sum;j>=0;j--) 12 { 13 for(int k=0;k<=tt-1;k++) 14 { 15 if(j>=k+1)dp[now][j]=max(dp[now][j],dp[now][j-k-1]+dp[to][k]+edge[i].val); 16 } 17 } 18 } 19 return sum+1; 20 }
来让我们慢慢分析为什么要这么写,
1.那个sum是什么
sum返回的其实是点的数量(这样好理解),或者理解成子树的边的个数+1也行,为什么要+1,因为对于叶节点,它下面没有边,返回的不可能是0,只能是1,所以只能加一
2.枚举范围是为什么??
首先看j的范围,我什么是sum呢?记得sum是什么吗,是子树边,加一,刚好是父节点要选的,所以可以直接就是sum,
看k的范围,为什么是tt-1,也是因为sum的意义,减1了才是子树的边数量
那么为什么是j-k-1呢,因为如果你在当前子节点选了k个,同时你也把根节点和子节点的边也选了,所以实际上选的是k+1个边,
然后j>=k+1就是怕超范围
结束QAQ
上代码
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define N 111 5 #define _ 0 6 using namespace std; 7 int n,q,u,v,w,cnt=1; 8 int head[N],dp[N][N]; 9 struct star{int to,nxt,val;}edge[N*2]; 10 inline void add(int u,int v,int w) 11 { 12 edge[cnt].nxt=head[u]; 13 edge[cnt].to=v; 14 edge[cnt].val=w; 15 head[u]=cnt++; 16 } 17 int DP(int now,int fa) 18 { 19 int sum=0; 20 for(int i=head[now],to;i!=-1;i=edge[i].nxt) 21 { 22 to=edge[i].to; 23 if(to==fa)continue; 24 int tt=DP(to,now); 25 sum+=tt; 26 for(int j=min(sum,q);j>=0;j--) 27 { 28 for(int k=0;k<=tt-1;k++) 29 { 30 if(j>=k+1)dp[now][j]=max(dp[now][j],dp[now][j-k-1]+dp[to][k]+edge[i].val); 31 } 32 } 33 } 34 return sum+1; 35 } 36 int main() 37 { 38 memset(head,-1,sizeof(head)); 39 scanf("%d%d",&n,&q); 40 for(int i=1;i<n;i++) 41 scanf("%d%d%d",&u,&v,&w), 42 add(u,v,w),add(v,u,w); 43 DP(1,-1); 44 printf("%d",dp[1][q]); 45 return ~~(0^_^0); 46 }