#include<stdio.h> #include<string.h> typedef struct room { int bug, po; } room; room R[110];//记录原始数据 int vis[110], next[110], n, m, tree[110][110]; int dp[110][110];//dp[i][j]代表以第i个节点为根节点的树在拥有j个士兵的情况下所能获得首脑的最大概率 int Max(int x, int y) { return x>y?x:y; } void dfs(int a) { int i, j, k, cost; vis[a] = 1; cost = (R[a].bug + 19) / 20;//记录该节点所需要的士兵个数 for(i = cost; i <= m; ++i)//初始化该节点能获得的概率 dp[a][i] = R[a].po; for(i = 0; i <= next[a]; ++ i) { int b = tree[a][i];//子节点的编号 if(!vis[b]) { dfs(b);//求子节点能获得的最大概率 for(j = m; j >= cost; --j)//回溯求父节点的最大概率 for(k = 1; j+k <= m; ++k) { if(dp[b][k])//如果说子节点有概率存在,更新父节点的值 dp[a][j+k] = Max(dp[a][j+k], dp[a][j] + dp[b][k]); } } } return ; } int main() { int i, j, k; while(scanf("%d%d", &n, &m) != EOF && ( n!= -1 || m != -1)) { for(i = 1; i <= n; ++i)//编号从1开始 scanf("%d%d", &R[i].bug, &R[i].po); memset(next, -1, sizeof(next)); for(i = 0; i < n-1; ++i) { int a, b; scanf("%d%d", &a, &b); tree[a][++next[a]] = b;//next数组保存节点的子节点的个数 tree[b][++next[b]] = a;//tree保存该节点的子节点的编号 } if(!m ) { printf("0\n"); } else { memset(dp, 0, sizeof(dp)); memset(vis, 0, sizeof(vis)); dfs(1); printf("%d\n", dp[1][m]); } } return 0; }
思路:首先该虫穴的图形可以明显看得出是一个树形图,对于从上往下每一个节点都可以选择要或是不要,这有点类似于背包,若是要则需要扣除一定的士兵,并且能够获得一定的概率,所以可以用dp来做,一个dp【i】【j】表示以编号i为根节点的树在拥有j个士兵的情况下所能获得的最大概率,从树的根节点从上往下开始遍历,再从叶子几点开始回溯,因为对于叶子节点来说,在有1~m个士兵的时候所能获得的概率只有一种情况(士兵数大于该节点所需要的士兵,获得该点的概率,否则为0),接下来回溯父节点的dp状况,对于父节点来说,本身在士兵个数大于消耗的情况下能获得该点的概率,但是有可能该点的概率值在某个士兵的个数下可以获得该点的概率值并加上子节点的概率值,所以需要更新该点的概率值,如此一直回溯到该数的根节点即dp【1】【m】,即为答案。
难点:该题的难点在于找到dp方程, 即背包的思路,穷举一个节点所能获得概率值的所有情况,接着就是建树进行遍历了。