题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1011
题目大意:给一棵树,有n个顶点,n-1条边,每个顶有一些bugs,现在要用炸弹(就用炸弹吧)轰炸,一个炸弹最多炸死20只bug,如果某个地方为0只bug,可以滚到下一个节点去炸,n<=100,边m<=100
解题思路:比较简单的树形DP+背包。本题的依赖关系可以理解成树边,到达子节点必须先经过父节点,本质是一样的,建立起一棵树就开始如何进行dp,解题核心是搞清楚如何从子节点获取信息。
一个子节点就可以返回m个状态,每个状态表示容量为j(j<=m)时炸最多的bug,而一个子节点中只可以选择一个状态进行转移,每个节点有若干个子节点,问题就转换为分组背包,几个子节点就是几个分组背包,体积是选几个地点,价值为Boos的可能性。本题要特判m为0时,直接输出0.
状态转移方程: dp[v][1] = bug[v]; (v为叶子节点)
dp[v][j] = max(dp[v][j],dp[v][j-i] + dp[k][i] );(v为非叶子节点,j表示用户个数,i为容量,k为v的子节点,)
算法复杂度O(sum(num[i],num[s])) (num[i]为某个节点的叶子子孙个数,num[s]为i的子节点的叶子子孙个数)
测试数据:
5 10
50 10
40 10
40 20
65 30
70 30
1 2
1 3
2 4
2 5
1 1
20 7
5 0
50 10
40 10
40 20
65 30
70 30
1 2
1 3
2 4
2 5
4 1
0 10
0 10
0 10
0 10
1 2
2 3
1 4
-1 -1
代码:
#include <stdio.h>
#include <string.h>
#include <vector>
using namespace std;
#define MAX 110
#define max(a,b) (a)>(b)?(a):(b)
int val[MAX],need[MAX];
vector<int> tree[MAX];
int cnt,n,m,dp[MAX][MAX],vis[MAX];
void Tree_DP(int s){
if (vis[s]) return ;
vis[s] = 1;
int i,j,k,tp,temp;
for (i = need[s]; i <= m; ++i)
dp[s][i] = val[s];
for (i = 0; i < tree[s].size(); ++i) {
tp = tree[s][i];
if (vis[tp]) continue;
Tree_DP(tp);
for (j = m; j >= need[s]; --j)
for (k = 1; k + j <= m; ++k)
if (dp[tp][k]) {
temp = dp[s][j] + dp[tp][k];
dp[s][j+k] = max(dp[s][j+k],temp);
}
}
}
int main()
{
int i,j,k,a,b;
while (scanf("%d%d",&n,&m),n + m != -2){
for (i = 1; i <= n; ++i){
scanf("%d%d",&need[i],&val[i]);
tree[i].clear();
need[i] = (need[i] + 19) / 20;
}
for (i = 1; i < n; ++i){
scanf("%d%d",&a,&b);
if (a < b)
tree[a].push_back(b);
else
tree[b].push_back(a);
}
if (m == 0) {
printf("0\n");
continue;
}
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
Tree_DP(1);
printf("%d\n",dp[1][m]);
}
return 0;
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。