#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define MAXN 105 using namespace std; int n,k,tot; int a[MAXN],h[MAXN]; int f[MAXN][MAXN<<1][2];//那个子树,移动几次 是否回到根 struct node{ int from,to,next; }e[MAXN<<1]; void init(){ tot=0; memset(h,-1,sizeof(h)); memset(f,0,sizeof(f)); } void add(int x,int y){ tot++; e[tot].from=x; e[tot].to=y; e[tot].next=h[x]; h[x]=tot; } //0表示回来根 1表示不必 int dfs(int now,int fa){ for(int i=0;i<=k;i++)f[now][i][0]=f[now][i][1]=a[now]; for(int i=h[now];i!=(-1);i=e[i].next){ if(e[i].to!=fa){ dfs(e[i].to,now); //开始dp了喔 for(int j=k;j>=0;j--){ for(int p=1;p<=j;p++){ if(p>=2)f[now][j][0]=max(f[now][j][0],f[now][j-p][0]+f[e[i].to][p-2][0]); if(p>=1)f[now][j][1]=max(f[now][j][1],f[now][j-p][0]+f[e[i].to][p-1][1]); if(p>=2)f[now][j][1]=max(f[now][j][1],f[now][j-p][1]+f[e[i].to][p-2][0]); } } } } } int main(){ while(scanf("%d%d",&n,&k)==2){ for(int i=1;i<=n;i++)scanf("%d",&a[i]); init(); for(int i=1;i<n;i++){ int x,y;scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs(1,1); cout<<f[1][k][1]<<endl; } }
嘛,比较难想的dp(其实不是难想啦....就是复杂一点而已)
嘛直接先套树形背包模板 但发现不足以表达所有状态....
于是乎再加一维!!!!(是否一定在根节点)
然后转移就好了
1.一定在根节点
1.其他子节点先到根,然后再转移当前子树
2.不一定在根节点
1.先往其他子树绕一圈回来,然后回来后再直接往下面走
2.从当前子树绕一圈回来,然后再接着走
然后这题就结束了