显然是一道树上的分配问题。自然而然想到树上的背包。
但要注意,这道题有两种情况。
1、往下遍历,并最后返回根节点。
2、往下遍历,并停留在某处结束,不返回根节点。
令第一种为f[x],第二种为g[x]。
求g[x]时,要枚举究竟是哪个儿子是往下遍历,最后停留在某处结束,不返回根节点的。
所以复杂度为O(N*N*K*K)
【教训】
在叶子这里的边缘状态设置:
f[x][0]=f[x][1]=f[x][2]=...=f[x][K]=apple[x]
而不能是
f[x][0]=apple[x],f[x][1]=f[x][2]=...=f[x][K]=0。
因为在叶子处剩余一些步数也是合法的。否则会出错。
【代码】
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N=205;
vector<int> a[N];
int f[N][N],g[N][N],ff[N][N],s[N];
int n,m;
void dfs(int x,int fa)
{
if (a[x].size()==1 && a[x][0]==fa)
{
f[x][0]=g[x][0]=s[x];
return;
}
int i,j,k,y,p,t,ss;
for (i=0;i<a[x].size();i++)
{
y=a[x][i];
if (y==fa) continue;
dfs(y,x);
}
g[x][0]=s[x];
for (p=0;p<a[x].size();p++)
{
t=a[x][p];
if (t==fa) continue;
memset(ff,0,sizeof(ff));
ff[0][0]=s[x];
for (i=1;i<a[x].size();i++)
{
y=a[x][i-1+(i>p)];
for (j=0;j<=m;j++)
{
for (k=0;j-k-2>=0;k++)
ff[i][j]=max(ff[i][j],ff[i-1][k]+f[y][j-k-2]);
ff[i][j]=max(ff[i][j],ff[i-1][j]);
}
}
ss=a[x].size()-1;
for (j=1;j<=m;j++)
for (k=0;k<=j-1;k++)
g[x][j]=max(g[x][j],ff[ss][j-k-1]+g[t][k]);
}
memset(ff,0,sizeof(ff));
ff[0][0]=s[x];
for (i=1;i<=a[x].size();i++)
{
y=a[x][i-1];
for (j=0;j<=m;j++)
{
for (k=0;j-k-2>=0;k++)
ff[i][j]=max(ff[i][j],ff[i-1][k]+f[y][j-k-2]);
ff[i][j]=max(ff[i][j],ff[i-1][j]);
}
}
ss=a[x].size();
f[x][0]=s[x];
for (j=0;j<=m;j++)
f[x][j]=max(f[x][j],ff[ss][j]);
for (i=0;i<=m;i++)
{
f[x][i]=max(f[x][i-1],f[x][i]);
g[x][i]=max(g[x][i],g[x][i-1]);
}
}
int main()
{
int i,x,y,ans;
freopen("in","r",stdin);
while (scanf("%d%d",&n,&m)!=EOF)
{
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
ans=0;
for (i=1;i<=n;i++)
a[i].clear();
for (i=1;i<=n;i++)
scanf("%d",&s[i]);
for (i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
a[x].push_back(y);
a[y].push_back(x);
}
dfs(1,0);
ans=max(f[1][m],g[1][m]);
printf("%d\n",ans);
}
}