题意是有一颗树,树上每个结点有虫子和宝藏,每个勇士能杀死20只虫子,杀死虫子才能获得宝藏。
很明显的树形dp dp[u][i]代表以u为根的子树花费i个勇士所获得的最大价值。
那么dp[u][i]=max(dp[u][i],dp[v][k]+dp[u][i-k]) (1<=k<=j)
状态转移方程并不难想,也并不难写,但是这题有一点恶心的是:即使某个结点没有虫子也要派个人过去收集金币。
解决办法是:状态转移的时候至少派一个个去,方程里即k>=1。不能转移子节点k=0的状态!!!!!
细节请看代码:
#include<bits/stdc++.h> using namespace std; const int N=1e2+10; int n,m; vector<int> G[N]; int dp[N][N],ct[N],vl[N]; int bugs(int x) { return (int)ceil((double)x/20); } void dfs(int x,int fa,int m) { if (m<=0 || m<bugs(ct[x])) return; for (int i=bugs(ct[x]);i<=m;i++) dp[x][i]=vl[x]; for (int i=0;i<G[x].size();i++) { int y=G[x][i]; if (y==fa) continue; dfs(y,x,m-bugs(ct[x])); for (int j=m;j>=bugs(ct[x]);j--) for (int k=1;k<=j;k++) dp[x][j]=max(dp[x][j],dp[y][k]+dp[x][j-k]); } } int main() { while (scanf("%d%d",&n,&m)==2 && n!=-1) { for (int i=1;i<=n;i++) scanf("%d%d",&ct[i],&vl[i]); for (int i=1;i<=n;i++) G[i].clear(); for (int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); G[x].push_back(y); G[y].push_back(x); } memset(dp,-0x3f,sizeof(dp)); dfs(1,0,m); int ans=0; for (int i=1;i<=m;i++) ans=max(ans,dp[1][i]); cout<<ans<<endl; } return 0; }
从HDU讨论区扒来的一个数据:
8 2 0 0 0 9 0 8 0 4 0 7 0 3 0 2 0 1 1 2 1 3 2 4 2 5 4 6 6 7 7 8 27