POJ2342AnniversaryParty

一道基础的树形DP,为我们基本讲解出树形DP的特点。

按照我浅薄的理解,树形DP是把寻找最优算法放到树上进行,先是找到子根,然后往上递推回去,需要记录每个点的父根。

这道题中我采取的方法是用数组记录一个节点的父根,也可以用vector,vector比较适用于记录自己的多子根,输入的时候进行处理,i对应的father[i]的值就是其父根。因为初始化为0所以最后值为0的i就是整棵树的父根。

接下来就找到整棵树的父根,然后开始深搜,搜到最后的子根及其对应的父根,对于其父根而言,可以选择去或不去,去的话子根就不能去,不去的话就选择子根去或不去的最大值。

这里就引出了最重要的状态转移方程,因为这道题里每个根只有去或不去两种可能,因此设置dp[i][j],i是对应的根,j只有两个值0或1,0代表不去,1代表去,值自然就是这种情况下的最优解(即最多人去的)。从下往上递归,因此状态转换方程是:

 

dp[node][1]+=dp[i][0];

dp[node][0]+=std::max(dp[i][0],dp[i][1]);

 

意思就是假如父根去了,只能选择不去的子根;父根不去,就选择去或不去最大的子根情况。然后一路推上去。


/*
 * POJ2342AnniversaryParty.cpp
 *
 *  Created on: 2014年7月8日
 *      Author: Prophet
 */
#include<cstdio>
#include<string.h>
#include<algorithm>
const int maxn = 6005;
int dp[maxn][2];
int father[maxn];//i对应的元素是i的父根
bool visit[maxn];//记录是否询问过
int n;
void dfs(int node);
int main(){
	while(scanf("%d",&n)!=EOF){
		for(int i=1;i<=n;i++)
			scanf("%d",&dp[i][1]);
		int l,k;
		int root=0;//整棵树的父根
		memset(father,0,sizeof(father));
		while(scanf("%d%d",&l,&k),l+k>0){
			father[l]=k;
		}
		for(int i=1;i<=n;i++){//找出整棵树的父根
			if(!father[i]){
				root = i;
				break;
			}
		}
		memset(visit,false,sizeof(visit));
		dfs(root);
		printf("%d\n",std::max(dp[root][1],dp[root][0]));
	}
}

void dfs(int node){
	visit[node]=true;
	for(int i=1;i<=n;i++){
		if(!visit[i]&&father[i]==node){//如果该点没被以之为父根且其父根为node,即node的子树且还没到过
			dfs(i);//一直到找到最后一个节点(i)及其父亲(node)
			dp[node][1]+=dp[i][0];
			dp[node][0]+=std::max(dp[i][0],dp[i][1]);
		}
	}
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值