题目: 每个人有个rating值, 总rating值为在场的所有人的rating值之和,但有个要求: 每个人都不能和他的直接上司待在一起,问最大可能的rating值是多少?
分析: 所有人的关系构成了一棵树,我们可以用d[i]表示已i为根节点的子树的rating值之和。
节点i有两种决策: 选和不选。 如果选节点i,则问题转化为 i 的所有孙子rating值之和;若不选 i 则问题转换为 i 的所有儿子的rating值之和。转移方程为:
d[ i ] = max { rating [ i ] + ∑ d[ j ] , ∑ d[ k ] } (rating[ i ] 表示 i 的rating值, j 为 i 的孙子 , k 为 i 的儿子)
这类问题就是所谓的树形DP。
代码:
#include <iostream>
#include <cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=6050;
int d[maxn],in[maxn],sum[maxn]; // 这里d[i]是rating [i] , sum[i]是上面说的d[i] , 变量有些混乱了~~~
vector<int>son[maxn]; //son[i]存放i的所有儿子
int N;
int dfs(int u){
if(sum[u]!=-1) return sum[u]; //记忆化搜索
int sn=0,gsn=0; //sn指u的所有儿子rating之和 , gsn指u的所有孙子rating之和
for(int i=0;i<son[u].size();i++){
int v=son[u][i];
sn += dfs(v);
for(int j=0;j<son[v].size();j++){
gsn += dfs(son[v][j]);
}
}
return sum[u]=max(sn,gsn+d[u]);
}
int main()
{
while(~scanf("%d",&N),N){
for(int i=0;i<=N;i++) son[i].clear(),in[i]=0,sum[i]=-1;
int x,y;
for(int i=1;i<=N;i++) scanf("%d",&d[i]);
for(int i=1;i<N;i++){
scanf("%d%d",&x,&y);
son[y].push_back(x); in[x]++;
}
int u=1;
for(int i=1;i<=N;i++){
if(in[i]==0) { u=i; break; }
}
int ans=dfs(u);
printf("%d\n",ans);
}
return 0;
}
用vector记录所有的儿子并不是很好的做法,其实可以从另一个角度看,也可以从i 节点出发去更新它的 父亲 和祖父的 d [ i ]值 ,这样每个节点需几录它的父亲是谁就可以了,这样做不论在时间还是空间上都是 一个很有效的该进。
同样给出代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int maxn=6010;
int fa[maxn],rate[maxn],son[maxn],gson[maxn],sonnum[maxn]; //分别指父亲节点,该节点权值,所有儿子节点权值之和,所有孙子节点权值加上它自身权值之和,儿子的个数
queue<int>que;
int N;
void Init(){
memset(fa,0,(N+1)*sizeof(int));
memset(sonnum,0,(N+1)*sizeof(int));
memset(son,0,(N+1)*sizeof(int));
memset(gson,0,(N+1)*sizeof(int));
while(!que.empty()) que.pop();
}
int main()
{
while(scanf("%d",&N),N){
Init();
for(int i=1;i<=N;i++) scanf("%d",&rate[i]);
int L,K;
for(int i=1;i<N;i++){
scanf("%d%d",&L,&K);
fa[L]=K,sonnum[K]++;
}
for(int i=1;i<=N;i++) {
if(!sonnum[i]) que.push(i);
}
int u,f;
while(!que.empty()){
u=que.front(); que.pop();
gson[u] += rate[u];
f=fa[u];
son[f] += max(son[u],gson[u]);
gson[f] += son[u];
if(f && --sonnum[f]==0) que.push(f); //注意向上更新的顺序,只有当该节点的所有儿子节点更新完了(即 --sonnum[f]==0),才用它去更新它父亲,保证得到正确解
}
printf("%d\n",max(son[u],gson[u]));
}
return 0;
}