您的位置:首页 > 移动开发

POJ 2486 Apple Tree (树形DP,树形背包)

2015-09-20 21:55 429 查看
题意:给定一棵树图,一个人从点s出发,只能走K步,每个点都有一定数量的苹果,要求收集尽量多的苹果,输出最多苹果数。

思路:

  既然是树,而且有限制k步,那么树形DP正好。

  考虑1个点的情况:(1)可能在本子树结束第k步(2)可能经过了j步之后,又回到本节点(第k步不在本子树)

  第二种比较简单,背包一下,就是枚举给本节点的孩子t多少步,收集到最多苹果数。第一种的话要求第k步终止于本节点下的某个子树中,那么只能在1个孩子子树中,所以应该是【其他孩子全部得走回来】+【本孩子不要求走回来】 or 【其他某个孩子中不走回来】+【本节点走回来】。

  用两个DP数组区分下“回”与“不回”就行了,注意,“不回”只能有1个孩子不要求其走回来,“回”是全部回。而“不要求回来”收集到的苹果数必定大于等于“要求回来”。

//#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <iostream>
#define pii pair<int,int>
#define INF 0x3f3f3f3f3f3f3f3f
#define LL  long long
using namespace std;
const int N=210;
struct node
{
int from,to,next;
node(){};
node(int from,int to,int next):from(from),to(to),next(next){};
}edge[N*2];
int edge_cnt, head
, w
;
void add_node(int from,int to)
{
edge[edge_cnt]=node(from, to, head[from]);
head[from]=edge_cnt++;
}
/*
dp[][][1]   记录每次都回到本节点的。
dp[][][0]   记录仅1次不回到本节点的。
*/
int dp

[2];
void DFS(int t,int far,int m)
{
node e;
for(int j=0; j<=m; j++)    dp[t][j][0]=dp[t][j][1]=w[t];    //既然能到这,至少带上本节点
if(m==0)     return;
for(int i=head[t]; i!=-1; i=e.next)
{
e=edge[i];
if( e.to^far )
{
DFS(e.to, t, m-1);
for(int j=m; j>0; j-- )
{
for(int k=0; k+2<=j; k++ )
{
//所有分支都回。
dp[t][j][1]=max( dp[t][j][1], dp[t][j-k-2][1]+dp[e.to][k][1] );
//本分支要回,但在其他分支不回。因为已经有1个不回了,所以更新在‘[0]’中。
dp[t][j][0]=max( dp[t][j][0], dp[t][j-k-2][0]+dp[e.to][k][1] );
}
for(int k=0; k+1<=j; k++ )
{
//不回,但其他分支就必须全回。
dp[t][j][0]=max( dp[t][j][0], dp[t][j-k-1][1]+dp[e.to][k][0] );
}
}
}
}
}

int main()
{
//freopen("input.txt", "r", stdin);
int n, K, a, b;
while(~scanf("%d%d",&n,&K))
{
edge_cnt=0;
memset(head, -1, sizeof(head));

for(int i=1; i<=n; i++) scanf("%d",&w[i]);
for(int i=1; i<n; i++)
{
scanf("%d%d",&a,&b);
add_node(a,b);
add_node(b,a);
}
DFS(1, -1, K);
cout<<dp[1][K][0]<<endl;
}
return 0;
}


AC代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: