树形DP学习心得

我们发现关于树形DP的概念很少,动态规划的概念有很多。神勇鑫认为(对菜鸟而言 ),动规就是通过枚举,筛选,递归等方法一步一步将大问题划分成小问题,在小问题中筛选出的结果即为这个大问题的最优子结构,再返回共同后成最优解。动态规划的形成一般会牵扯到二维数组,用二维数组可以表示一个变量的两种状态,如a[1][0]和a[1][1]可以表示a数组中第一个元素的两种情况(就下面这题而言,它可以表示第一个人出席或者没有出席)。
树形动态规划属于动态规划的一种,变量之间具有相关关系。运用递归,可以根据这个相关关系从顶层搜索到底层,对底层进行最有子结构的计算,然后反馈。
下面是一个树形DP的经典例题,附上自己的代码。如果不够清楚,可以在链接中查看原题的题解。

例题:没有上司的舞会
题目描述
某大学有N个职员,编号为1~N。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。
输入输出格式
输入格式:

第一行一个整数N。(1<=N<=6000)
接下来N行,第i+1行表示i号职员的快乐指数Ri。(-128<=Ri<=127)
接下来N-1行,每行输入一对整数L,K。表示K是L的直接上司。
最后一行输入0 0
输出格式:
输出最大的快乐指数。
样例输入: 样例输出:
7 5
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0

题目分析:

每个人都有出席或者不出席两种状态,如果一个人出席,可能会对其他人有影响,那么我们就会想到动规来解题,每个人之间具有从属关系,我们可以用一个二维数组relation[maxn][maxn]表示。

eg. relation[1][1]=5,relation[1][2]=3。表示1是个BOSS,手下有5和3两个员工,1的状态受到5和3的影响,5和3又会受到他们下属的影响(relation[5][number],relation[3][number]),DP方程接收一个对象,就对这个对象的下属进行递归,下属的下属下属的下属(下属还真X多~~),直到没下属,求出出席还是不出席两种状态的最优解。

但是对于每个BOSS来说,直接下属人数是不确定的,不方便递归。熟练的话可以用vector函数(~~emmm是真的想用,可是还不会用),这个函数定义了一个容器,相当于一个动态数组,不定长,你可以一直往里面添加元素,用relation[number].size就可以返回添加了多少,方便的多。

eg . vector relation[maxn]; //声明maxn个老板的容器,尽情的添加它的员工吧,码农君。
这里我用结构体数组表示从属关系,结构体元素count用来表示这个BOSS下面有多少员工,也很方便。

代码呈上。

#include<stdio.h>
#include<iostream>  
#include<stdlib.h>
#include<string.h>
#define maxn 6000+5
using namespace std;
struct node{
	  int count;    //用来表示这位大咖有多少员工
	  int emp[maxn/5];                     //测试数据应该不会too变态,所以少弄了些员工,不然容易爆内存
}link[maxn];    //用结构体数组表示他们的从属关系
int tree[maxn][2]={0};   //用来表示出席不出席两种状态对应的欢乐度   0不出席 1出席
int happy[maxn]={0},bigboss[maxn]={0};
int n,ans=0;
void dfs(int a){       
	  tree[a][0]=0;
	  tree[a][1]=happy[a];  //初始化
	  for(int i=1;i<=link[a].count;i++){
	   dfs(link[a].emp[i]);   
	   tree[a][0]+=max(tree[link[a].emp[i]][0],tree[link[a].emp[i]][1]);  //a不出席是下属不出席,下属出席的max.
	   tree[a][1]+=tree[link[a].emp[i]][0];   //a出席即下属不出席的最优解
	   ans=max(tree[a][0],tree[a][1]);   
	  }
} 
int main(){
	  sizeof(link,0,sizeof(link));
	  int root;
	  scanf("%d",&n);
	  //输入欢乐度
	  for(int i=1;i<=n;i++){
	    scanf("%d",&happy[i]);
	  }
	  //输出化关系数组
	  for(int i=0;i<n;i++){
	    int e,b;
	    scanf("%d%d",&e,&b);
	    bigboss[e]=1;            //bigboss用来找出最大的老板,员工被标记为1,数值为0的即是BIG BOSS。
	    link[b].count++;         //员工数++
	    link[b].emp[link[b].count]=e;
	  }
	  //找到BIG BOSS
	  for(int i=1;i<=n;i++){
   		 if(bigboss[i]==0){
    		 root=i;
     		break;
    		}
	  }
	  dfs(root);   //从BIG BOSS开始
	  printf("%d",ans);
 	  return 0;
}

买一赠一送个题:加分二叉树(比上面的难度大,需要考虑二叉树的特征)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值