题意:
有一颗以1为根结点的树,每个结点有ai的敌对兵力,以及bi价值的物品,一开始有m个自方兵力,每个己方兵力可以战胜20个敌对兵力。入口在1结点,只有战胜一个结点,才能获得这个结点的物品,以及进入这个结点的子结点。为了节约时间,每个结点都要留下对抗的兵力,其余继续前进,问最多可以获得多大价值的物品。
题解:
典型的树形dp。dp[i][j]表示i结点剩余j兵力的情况下最多能获得多大价值的物品。dp[i][j]=max(dp[i][j],dp[i][j-k]+dp[u][k]);其中u为i的孩子。由于一个结点可能有多个子结点,就变成了01背包的问题。
注意:一个结点的敌对兵力为0时,不需要再这个结点驻守兵力,但必须有己方兵力经过才能获得物品。
特殊样例:
5 2
0 1
0 1
0 5
0 1
0 2
1 2
1 3
2 4
2 5
答案是9。一个兵从1到达3,获得价值5;一个兵从1到达5,获得价值3;再加上根结点1的价值1。
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <cctype>
using namespace std;
const int maxn=1e2+10;
struct node{
int foe,w;
}f[maxn];
int dp[maxn][maxn];
vector<int>e[maxn];
void dfs(int i,int pre,int m)
{
int j,k,p,q,u;
for(j=f[i].foe;j<=m;j++)dp[i][j]=f[i].w;
for(j=0;j<e[i].size();j++)
{
u=e[i][j];
if(u!=pre)
{
dfs(u,i,m);
for(p=m;p>=f[i].foe;p--)
{
for(q=0;q+f[i].foe<=p;q++)
dp[i][p]=max(dp[i][p],dp[i][p-q]+dp[u][q]);
}
}
}
dp[i][0]=0;//没有驻留兵力的时候,即使该点的敌对兵力为0也无法获得物品。
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==-1&&m==-1)break;
int i,j,k,a,b;
for(i=0;i<n;i++)
{
e[i].clear();
scanf("%d%d",&a,&f[i].w);
f[i].foe=a==0?0:(a-1)/20+1;
}
for(i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
a--;b--;
e[a].push_back(b);
e[b].push_back(a);
}
//for(i=0;i<n;i++)for(j=0;j<e[i].size();j++)printf("*%d %d\n",i,e[i][j]);
memset(dp,0,sizeof(dp));
dfs(0,-1,m);
printf("%d\n",dp[0][m]);
}
return 0;
}