HDU-1011 Starship Troopers(树形背包)

Problem Description
You, the leader of Starship Troopers, are sent to destroy a base of the bugs. The base is built underground. It is actually a huge cavern, which consists of many rooms connected with tunnels. Each room is occupied by some bugs, and their brains hide in some of the rooms. Scientists have just developed a new weapon and want to experiment it on some brains. Your task is to destroy the whole base, and capture as many brains as possible.

To kill all the bugs is always easier than to capture their brains. A map is drawn for you, with all the rooms marked by the amount of bugs inside, and the possibility of containing a brain. The cavern’s structure is like a tree in such a way that there is one unique path leading to each room from the entrance. To finish the battle as soon as possible, you do not want to wait for the troopers to clear a room before advancing to the next one, instead you have to leave some troopers at each room passed to fight all the bugs inside. The troopers never re-enter a room where they have visited before.

A starship trooper can fight against 20 bugs. Since you do not have enough troopers, you can only take some of the rooms and let the nerve gas do the rest of the job. At the mean time, you should maximize the possibility of capturing a brain. To simplify the problem, just maximize the sum of all the possibilities of containing brains for the taken rooms. Making such a plan is a difficult job. You need the help of a computer.

Input
The input contains several test cases. The first line of each test case contains two integers N (0 < N <= 100) and M (0 <= M <= 100), which are the number of rooms in the cavern and the number of starship troopers you have, respectively. The following N lines give the description of the rooms. Each line contains two non-negative integers – the amount of bugs inside and the possibility of containing a brain, respectively. The next N - 1 lines give the description of tunnels. Each tunnel is described by two integers, which are the indices of the two rooms it connects. Rooms are numbered from 1 and room 1 is the entrance to the cavern.

The last test case is followed by two -1’s.

Output
For each test case, print on a single line the maximum sum of all the possibilities of containing brains for the taken rooms.

Sample Input
5 10
50 10
40 10
40 20
65 30
70 30
1 2
1 3
2 4
2 5
1 1
20 7
-1 -1

Sample Output
50
7

[分析]
第一次做树形背包,感觉很多地方都还没参透。
这一片博客仅仅为自己而写,因为还有很多问题没有解决。自己写的代码,和网上的代码很相似,但在关键的地方不一样了。

首先阐述一下当前理解的思路。希望将来能参透正确的思路.
一颗树,每个节点有花费和价值。
做一个二维dp数组,dp[i][j],表示以i号节点为根的子树,拥有j的资源,可以得到的最大价值。
这个思想出来后,就可以填dp数组了。
用dfs遍历所有节点。所以dfs函数是这个算法的核心。

对于某一个节点,首先有自己本身的价值,所以第一个for就是将本身价值填入表中。

下面有3个嵌套的for
第一个for表示需要遍历的子节点,目的是将子树的最大价值加入当前结点
第二个for有需要注意的地方,就是j是–的,这里的思想和01背包的第二层循环的道理一样(以一维来dp),因为要读取当前层之前的数据,但这一层的更新会影响到后面的更新,所以倒着来循环可以避免这个问题。(这句可能很难读懂)
第三个for就是读取子节点花费k点资源得到的价值了

[错误和不懂得地方]
网上的代码在连接树的时候将子节点和根节点都连接了,并且加入了vis数组
no[x].son.push_back(y);
no[y].son.push_back(x);

而我只是
no[x].son.push_back(y);
并且没有vis(因为思想不一样吧)

还有就是第三层嵌套循环也不一样(但我觉得思想一样啊,可是就是wa了)
正确:for (int k = 1; k<=j-no[x].cos; k++)
{
dp[x][j] = max(dp[x][j], dp[x][j-k]+dp[sn][k]);
}
错误:for (int k = no[sn].cos; j + k <= m; k++)
{
dp[x][j + k] = max(dp[x][j + k], dp[x][j]+dp[sn][k]);
}
他是以减的方式来遍历,我是以加的方式来遍历。

[代码]

“`

include

include

include

include

using namespace std;
int n, m;
struct Node
{
int cos,val;
vectorson;
}no[105];
int vis[105];
int dp[105][105];
void dfs(int x)
{
vis[x] = 1;
for (int i = no[x].cos; i <= m; i++)dp[x][i] = no[x].val;

for (int i = 0; i < no[x].son.size(); i++)
{
    int sn = no[x].son[i];
    if (vis[sn])continue;
    dfs(sn);
    for (int j = m; j>=no[x].cos; j--)
    {
        for (int k = 1; k<=j-no[x].cos; k++)
        {
            dp[x][j] = max(dp[x][j], dp[x][j-k]+dp[sn][k]);
        }
    }
}

}

int main()
{
while (scanf(“%d%d”, &n, &m) != EOF)
{
if (n == -1 && m == -1)return 0;
memset(dp, 0, sizeof(dp));
memset(vis, 0, sizeof(vis));
for (int i = 0; i < 105; i++)no[i].son.clear();
for (int i = 1; i <= n; i++)
{
scanf(“%d%d”, &no[i].cos, &no[i].val);
no[i].cos = (no[i].cos + 19) / 20;
}
for (int i = 1; i < n; i++)
{
int x, y;
scanf(“%d%d”, &x, &y);
no[x].son.push_back(y);
no[y].son.push_back(x);
}
if (m == 0)
{
printf(“0\n”);
continue;
}
dfs(1);
printf(“%d\n”, dp[1][m]);
}
}

[后记]
这篇文章就先放这里了,毕竟这道题已经花了很长的时间了,什么时候有时间再来做这题吧。
思想还没有参透。不过应该也很接近了。

猜你喜欢

转载自blog.csdn.net/q435201823/article/details/80205518