题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1011
题目大意:由n个节点构成一棵树,通过每个节点有消耗((bug+19)/20),每个节点还有一个价值,开始有士兵m人,问从1节点开始往下走最多获得多少价值
题目思路:树状dp,dp[i][j]表示以i节点为根节点且消耗j能量时能获得的最大价值。状态转移方程:dp[u][j] = max(dp[u][j],dp[u][j-k]+dp[v][k])
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
const int maxn = 110;
struct XX{
int num,val;
}nn[maxn];
struct Side{
int next,v;
}side[maxn*5];
int node[maxn],top;
void add_side(int u,int v){
side[top]=(Side){node[u],v};
node[u]=top++;
}
int dp[maxn][maxn],vis[maxn];
void dfs(int u){
vis[u] = 1;
int num = (nn[u].num+19)/20;
for(int i=num;i<=m;i++)
dp[u][i] = nn[u].val;
for(int i=node[u];i!=-1;i=side[i].next){
int v = side[i].v;
if(vis[v])continue;
dfs(v);
for(int j = m;j>=num;j--){
for(int k = 1;k+num<=j;k++){
dp[u][j] = max(dp[u][j],dp[u][j-k]+dp[v][k]);
}
}
}
}
int main(){
while(cin>>n>>m){
memset(dp,0,sizeof(dp));
memset(node,-1,sizeof(node));
memset(vis,0,sizeof(vis));
top=0;
if(n==-1&&m==-1)break;
for(int i=1;i<=n;i++)
cin>>nn[i].num>>nn[i].val;
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
add_side(a,b);
add_side(b,a);
}
if(m==0){printf("0\n");continue;}
dfs(1);
printf("%d\n",dp[1][m]);
}
return 0;
}
关于状态转移的方向。。dp做的少。。然后就出问题了Orz
dp[u][j] = max(dp[u][j],dp[u][j-k]+dp[v][k]);
dp[u][j] 需要dp[u][j-k]去更新他的值,所以j应该是减小的,这样更新j的时候就能确保j-k是没更新过的(就是01背包那样。。)