设f[i][j][k]表示给以i为根节点的子树分配j条可保留的树枝名额的时候,状态为k时能保留的最多苹果。
k有三种情况。
k=1:我只考虑子树的左叉,不考虑子树的右叉,此时子树能保留的最多的苹果。
k=2:我只考虑子树的右叉,不考虑子树的左叉,此时子树能保留的最多的苹果。
k=3:我既考虑子树的左叉,又考虑子树的右叉,此时子树能保留的最多的苹果。
这样状态转移方程就出来了。
f[i][j][1]=max(f[i][j][1],f[leftson[i]][j-1][3]+val[i][leftson[i]])
f[i][j][2]=max(f[i][j][2],f[rightson[i]][j-1][3]+val[i][rightson[i]])
f[i][j][3]=max(f[i][j][3],f[i][v][1]+f[i][j-v][2]) 其中v从0到j枚举。
最后f[1][q][3]就是最终的答案。
注意记忆化搜索。我因为这个T了四次。
#include<cstdio> #include<cctype> #include<algorithm> using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } struct Edge{ int next,to,val; }edge[100010]; int head[100010],num; int father[100010]; int size[100010]; inline void add(int from,int to,int val){ edge[++num]=(Edge){head[from],to,val}; head[from]=num; } void find(int x,int fa){ father[x]=fa; size[x]=1; for(int i=head[x];i;i=edge[i].next){ int to=edge[i].to; if(to!=fa){ find(to,x); size[x]+=size[to]; } } } int f[103][103][4]; void dfs(int x,int s){ if(size[x]==1||f[x][s][3]) return; int cnt=0; for(int i=head[x];i;i=edge[i].next){ int to=edge[i].to; if(to==father[x]) continue; cnt++; for(int v=0;v<size[to]&&v<s;++v){ dfs(to,v); f[x][v+1][cnt]=f[to][v][3]+edge[i].val; } } for(int v=0;v<=s;++v) f[x][s][3]=max(f[x][s][3],f[x][v][1]+f[x][s-v][2]); return; } int main(){ int n=read(),q=read(); for(int i=1;i<n;++i){ int from=read(),to=read(),val=read(); add(from,to,val); add(to,from,val); } find(1,1); dfs(1,q); printf("%d",f[1][q][3]); return 0; }