http://acm.hdu.edu.cn/showproblem.php?pid=1011
题意很纠结。。 第一遍看了题目之后以为是从根走到叶子结点不分叉,一交WA 。 再看题目,发现只是说同一个结点不能经过两次,但是可以“兵分几路”的思想去达到最大值。然后就是想到了对每个根结点u ,进行一次背包,但是这里还有一个地方需要注意,就是:如果要获得子结点的Brain 就必须要首先获得父节点的Brain,若不是这样的话,样例的最大值就应该是80 了。 由此得出状态转移方程: dp[u][j]:以u为根的子树中,用掉j个士兵时可以达到的最大值, dp[u][j] = max( dp[u][j] , dp[u][j - k]+dp[ son[u] ][k] ) ;
代码:
#include<stdio.h>
#include<string.h>
#include<vector>
#define MAX(a,b) (a)>(b)?(a):(b)
using namespace std;
int N , M ;
int W[110] , V[110] ;
vector<int> G[110] ;
int f[110] ;
int dp[110][110] ;
void Build(int u){
for(int i=0;i<G[u].size();i++){
int v = G[u][i] ;
if(v == f[u]) continue ;
f[v] = u ;
Build(v) ;
}
}
void DP(int u){
for(int i=0;i<G[u].size();i++){
int v = G[u][i] ;
if(v == f[u]) continue ;
DP(v) ;
for(int j=M ;j>=W[u];j--){
for(int k=1;k+W[u]<=j;k++){
int temp = dp[u][j-k] + dp[v][k] ;
if(dp[u][j] < temp){
dp[u][j] = temp ;
}
}
}
}
}
int main(){
int a, b ;
while(scanf("%d%d",&N,&M) == 2){
if(-1==N && M==-1) break ;
memset(dp, 0 ,sizeof(dp));
for(int i=1;i<=N;i++){
scanf("%d%d",&a,&b);
W[i] = a/20 ; V[i] = b ;
if(W[i]*20<a) W[i]++ ;
for(int j=W[i];j<=M;j++){
dp[i][j] = V[i] ;
}
}
for(int i=1;i<=N;i++){
G[i].clear();
f[i] = -1 ;
}
for(int i=1;i<N;i++){
scanf("%d%d",&a,&b);
G[a].push_back(b);
G[b].push_back(a);
}
if(M==0){ //必须有,因为每个结点至少需要一个士兵,即使不需要代价也是。
puts("0"); continue ;
}
Build(1); //建树
DP(1) ;
printf("%d\n",dp[1][M]);
}
return 0;
}