Apple Tree
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 7893 Accepted: 2642
Description
Wshxzt is a lovely girl. She likes apple very much. One day HX takes her to an apple tree. There are N nodes in the tree. Each node has an amount of apples. Wshxzt starts her happy trip at one node. She can eat up all the apples in the nodes she reaches. HX is a kind guy. He knows that eating too many can make the lovely girl become fat. So he doesn’t allow Wshxzt to go more than K steps in the tree. It costs one step when she goes from one node to another adjacent node. Wshxzt likes apple very much. So she wants to eat as many as she can. Can you tell how many apples she can eat in at most K steps.
Input
There are several test cases in the input
Each test case contains three parts.
The first part is two numbers N K, whose meanings we have talked about just now. We denote the nodes by 1 2 … N. Since it is a tree, each node can reach any other in only one route. (1<=N<=100, 0<=K<=200)
The second part contains N integers (All integers are nonnegative and not bigger than 1000). The ith number is the amount of apples in Node i.
The third part contains N-1 line. There are two numbers A,B in each line, meaning that Node A and Node B are adjacent.
Input will be ended by the end of file.
Note: Wshxzt starts at Node 1.
Output
For each test case, output the maximal numbers of apples Wshxzt can eat at a line.
Sample Input
2 1
0 11
1 2
3 2
0 1 2
1 2
1 3
Sample Output
11
2
题解:这道题我开始想的是用f[i][j]表示在i节点的时候已经走了j步时的取到的最多的苹果数。但是我发现这样做对于两个在不同子树中节点的LCA到根节点的这一段距离会重复计算。
所以我们把需要动规方程所表示的状态换一下:f[i][j]表示到节点i的时候在i的子树中已经走了j步时的取到的最多的苹果。
我们再考虑怎样用子树去更新:因为这样的情况比较多,所以我们可以考虑用背包来解决这个问题。
但是我们需要注意一个问题:那就是题目中说可能会走到一个节点以后就不再往回走了,对于这种问题,我们还需要在改进一下动规方程:f[i][j][0]表示到节点i的时候在i的子树中已经走了j步而且不能再回到这个节点取到的最多的苹果;f[i][j][1]就表示可以回来的。
那么动规方程就是:
f[i][j][1]=max(f[i][j][1],f[i][j-t][1]+f[son][t-2][1])(表示走到子节点再走回来,在走向子节点喝回来的过程中需要走两条边,所以减去)
f[i][j][0]=max(f[i][j][0],f[i][j-t][1]+f[son][t-1][1])(表示从另外的字节点走到根节点之后,走到son节点,不回来,由于只是下去没有再回来,所以只是-1)
f[i][j][0]=max(f[i][j][0],f[i][j-t][1]+f[son][t-2][1])(表示从送节点回到根节点后,再从根节点向下走不回来的时候的最大值,因为这个时候跟第一种一样来回总共走了两次,所以-2)
这样以后这个问题我们就可以解决了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N=110;
const int K=210;
vector <int> tr[N];
int t,n,k,a[N],f[N][K][2];
void dp(int x,int last)
{
int i,j,u,l;
for(i=0;i<tr[x].size();++i){
u=tr[x][i];
if(u==last) continue;
dp(tr[x][i],x);
for(j=k;j>=1;--j){
for(l=1;l<=j;++l){
f[x][j][1]=max(f[x][j][1],f[x][j-l][1]+f[u][l-2][1]);
f[x][j][0]=max(f[x][j][0],f[x][j-l][1]+f[u][l-1][0]);
f[x][j][0]=max(f[x][j][0],f[x][j-l][0]+f[u][l-2][1]);
}
}
}
}
int main()
{
while(scanf("%d%d",&n,&k)==2){
int i,j,x,y;
for(i=1;i<N;++i) tr[i].clear();
memset(f,0,sizeof(f));
for(i=1;i<=n;++i){
scanf("%d",&a[i]);
for(j=0;j<=k;++j){
f[i][j][0]=f[i][j][1]=a[i];
}
}
for(i=1;i<n;++i){
scanf("%d%d",&x,&y);
tr[x].push_back(y);
tr[y].push_back(x);
}
dp(1,0);
printf("%d\n",max(f[1][k][0],f[1][k][1]));
}
}