Poj 2486 Apple Tree (DP_树形DP(背包))

题目链接:http://poj.org/problem?id=2486


题目大意:给定一棵节点数为n的树,每个节点都放有一些苹果,现在从根节点1开始走,每走一条边算一步,每经过一个节点就能吃掉这个节点的苹果(吃掉就没了),问走m步最多能吃几个苹果?


解题思路:树形DP + 分组背包。花了一下午A掉这题,没有一点优越感,只觉得这题好猥琐。题目要求从根节点开始往下走,如果一路向下必碰南墙得不到最优解,会有回溯这个过程,因为这个过程问题变得复杂。从根节点开始往下模拟,每次选择一个分支,会有三种情况:1、人走到这个分支中去就不回来了 2、人走到这个分支中去但是还回来,人留在根中 3、人走到这个分支中区但是还回来,跑到其他分支中区。我们只有三种选择,不是be Or not to be。从根往叶子节点想,再从叶子节点往上更新答案,最后输出根结点选到的最优解就好。

    模型转换:由于每次可选1..m步往下走,这个即是费用,m是容量,每个节点的苹果数为价值,每个节点为一组物品,每组物品至多选一个物品。

    假设有dp[i][j][k],dp[i][j][0]表示以i为根,走了j步回到i节点能吃的的最大苹果数,dp[i][j][1]表示以i为根,走了j步不回到i节点能吃的最大苹果数,

    状态转移方程比较繁琐,看代码更容易懂,代码中加注释部分即是。我自己写的代码比较挫,在看别人的解题报告时改成了这个简洁的飘逸的代码。

    把题目AC了还远远不够,多看别人代码,多学习别人思想和代码中的闪光点。   


测试数据:

4 3
1 2 3 4
1 2
2 3
2 4

2 1 
0 11
1 2

3 2
0 1 2
1 2
1 3

3 0
1 2 3
1 2
1 3

3 3
1 2 3
1 2
1 3


代码:

#include <stdio.h>
#include <string.h>
#define MAX 210
#define max(a,b) (a)>(b)?(a):(b)


struct node {

	int v;
	node *next;
}*head[MAX],tree[MAX];
int n,m,ptr,ans,vis[MAX];
int val[MAX],dp[MAX][MAX][2];


void Initial() {

	ptr = 1;
	memset(dp,0,sizeof(dp));
	memset(head,0,sizeof(head));
}
void AddEdge(int x,int y) {

	tree[ptr].v = y;
	tree[ptr].next = head[x],head[x] = &tree[ptr++];
}
void Tree_DP(int v) {

	node *p = head[v];
	for (int i = 0; i <= m; ++i)
		dp[v][i][1] = dp[v][i][0] = val[v];


	while (p != NULL) {
		
		Tree_DP(p->v);
		for (int j = m; j >= 0; --j) 	//分组背包						
			for (int k = 0; k <= j; ++k) {
				//往p->v分支走一次得到结果不回到当前节点,人留在p->v分支中
				dp[v][j+2][0] = max(dp[v][j+2][0],dp[v][j-k][0]+dp[p->v][k][0]);
				//往p->v分支走一次得到结果回到当前节点,人留在v的其他分支中
				dp[v][j+1][1] = max(dp[v][j+1][1],dp[v][j-k][0]+dp[p->v][k][1]);
				//往p->v分支走一次得到结果又回到当前节点,人留在v节点
				dp[v][j+2][1] = max(dp[v][j+2][1],dp[v][j-k][1]+dp[p->v][k][0]);
			}	
		p = p->next;
	}
}		


int main()
{
	int i,j,k,a,b;


	while (scanf("%d%d",&n,&m) != EOF) {

		Initial();
		for (i = 1; i <= n; ++i)
			scanf("%d",&val[i]);
		for (i = 1; i < n; ++i) {
		
			scanf("%d%d",&a,&b);
			if (a < b) AddEdge(a,b);
			else AddEdge(b,a);
		}


		Tree_DP(1);
		printf("%d\n",dp[1][m][1]);
	}
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值