ZOJ - 3201 Tree of Tree (树形背包)
题意:有一棵树,树上每个结点都有一个权值,求恰好包含k个结点的子树的最大权值。
设dp[i][j]为以结点i为根的树中包含j个结点的子树的最大权值,则可以把这个结点下的每棵子树中所包含的所有子树的大小当做物品的重量,对应的最大权值当做物品的价值,则相当于在它的每颗子树的所有物品中任选一个进行更新,对每个状态取最大价值。
状态转移方程:$dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k])$
注:由于每棵子树下只能选一件物品,所以应当先枚举状态再枚举物品,这样就避免了同一棵子树下状态的累加。
复杂度$O(n^3)$,可用siz数组确定上界优化到$O(n^2)$。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=100+10; 5 int hd[N],a[N],ne,n,k,dp[N][N],siz[N]; 6 struct E {int v,nxt;} e[N<<1]; 7 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;} 8 void dfs(int u,int fa) { 9 memset(dp[u],-1,sizeof dp[u]); 10 siz[u]=1,dp[u][1]=a[u]; 11 for(int i=hd[u]; ~i; i=e[i].nxt) { 12 int v=e[i].v; 13 if(v==fa)continue; 14 dfs(v,u); 15 for(int j=siz[u]; j>=0; --j)if(~dp[u][j]) 16 for(int k=0; k<=siz[v]; ++k)if(~dp[v][k]) 17 dp[u][j+k]=max(dp[u][j+k],dp[u][j]+dp[v][k]); 18 siz[u]+=siz[v]; 19 } 20 } 21 22 int main() { 23 while(scanf("%d%d",&n,&k)==2) { 24 memset(hd,-1,sizeof hd),ne=0; 25 for(int i=0; i<n; ++i)scanf("%d",&a[i]); 26 for(int i=0; i<n-1; ++i) { 27 int u,v; 28 scanf("%d%d",&u,&v); 29 addedge(u,v); 30 addedge(v,u); 31 } 32 dfs(0,-1); 33 int ans=0; 34 for(int i=0; i<n; ++i)ans=max(ans,dp[i][k]); 35 printf("%d\n",ans); 36 } 37 return 0; 38 }