题目描述
某大学有N个职员,编号为1~N。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。
输入输出格式
输入格式:
第一行一个整数N。(1<=N<=6000)
接下来N行,第i+1行表示i号职员的快乐指数Ri。(-128<=Ri<=127)
接下来N-1行,每行输入一对整数L,K。表示K是L的直接上司。
最后一行输入0 0
输出格式:
输出最大的快乐指数。
这道题解法为动态规划,但是和一般的线性dp有所不同,这道题是在一棵树形结构上执行动态规划,对于这样的动态规划,我们称其为树形dp,在这种dp中,一般我们把节点(子树)作为动态规划的
阶段。然后节点体现在状态数组的
第一维。另外,树形dp的过程往往是用
递归形式实现的,因为我们更新节点的顺序是从深到浅,然后一般我浅的地方的状态还没有更新,怎么办,当然就要一层一层地去递归先去更新那个最浅的地方!
现在我们开始说这道题的内容,因为上司和下属共同形成了一棵有根树,并且相邻的两层不能同时出席舞会。
所以我们可以定义f[x][0]表示当x节点出席舞会时,整棵子树能获得的最大价值,当然,x的所有直接下属都不能参加舞会。
定义f[x][1]为当x不出席舞会时,整棵子树能获得的最大价值,当然,他的直接下属既可以参加又可以不参加。
这样,就可以有一个初步的动态规划思路,阶段便是节点,但是这里我们把原本应该是最外层循环的阶段写成递归,为的就是更方便的访问节点,并且思路更清晰;
然后面临的一个问题,如何寻找校长节点?
我们可以在输入时记录,有父节点的点一定不是校长,最后循环去排除,也可以用类似拓扑的办法。
下面上代码!
1 //Is mule is horse please pull out and six six.--Luo 2 #include<iostream> 3 #include<cmath> 4 #include<cstdlib> 5 #include<cstdio> 6 #include<cstring> 7 #include<string> 8 #include<algorithm> 9 #include<vector> 10 using namespace std; 11 const int MAXN=6001; 12 int hp[MAXN];//happy 快乐指数; 13 int f[MAXN][2]={0};//f[x][0]代表x不参加时它的子树能合成的最大价值; 14 //f[x][1]则表示x参加时它子树获得的最大价值; 15 bool notdaboss[MAXN]={0}; 16 int z,x,c,v,a,b,n,m; 17 vector<int>boss[MAXN];//存储树形结构; 18 //在树形dp中,一般把dp写成递归的形式; 19 int dfs(int now,bool tpi)//和f数组差不多; 20 { 21 if(f[now][tpi]) return f[now][tpi];//记忆 22 if(!boss[now].size()) return hp[now]*tpi;//如果没有子树,那就是叶子结点,可以直接返回相应值; 23 int ans=0; 24 if(tpi) 25 { 26 for(int i=0;i<boss[now].size();i++)//状态 27 { 28 int &next=boss[now][i]; 29 ans+=dfs(next,0);//如果根节点参加会议,那直接子节点一定不参加; 30 } 31 } 32 else 33 { 34 for(int i=0;i<boss[now].size();i++)//状态 35 { 36 int &next=boss[now][i]; 37 ans+=max(dfs(next,true),dfs(next,false));//如果这个节点不参加,那么子节点既可以参加又可以不参加; 38 } 39 } 40 return f[now][tpi]=tpi? ans+hp[now]:ans;//选不选的决策问题! 41 } 42 int main() 43 { 44 scanf("%d",&n); 45 for(int i=1;i<=n;i++) 46 { 47 scanf("%d",&hp[i]); 48 } 49 while(scanf("%d%d",&a,&b)&&a&&b) 50 { 51 boss[b].push_back(a); 52 notdaboss[a]=true;//寻找根节点 53 } 54 for(int i=1;i<=n;i++) 55 { 56 if(!notdaboss[i]) 57 { 58 m=i;break;//寻找根节点(类似于拓扑) 59 } 60 } 61 cout<<max(dfs(m,0),dfs(m,1)); 62 return 0; 63 }