第一次发个有点难度的。
首先根据题目的描述可以知道,这个是一个树,有这样一个关系,如果选择了某一个节点,那么就不能够选择他的孩子节点,但是不代表不选择他的子孙。
每一个节点都有一个价值,请问如何选择这棵树的节点使得他们的价值总和最大。
那么我们可以建立f[i][0]和f[i][1]分别表示选择第i个节点或者不选择,他和他的子树最大的价值总和。
如果不选择,则f[i][0]=Σf[x][1] (x是i的子节点)
如果选择,那么它的子节点就不能选择f[i][1]=w[i]+Σf[x][0]
如果该节点x为叶子节点,那么f[x][0]=0 f[x][1]=w[x]
这样就建立了一个关系式,那么经过记忆化搜索就能够完成计算,因为有一下关系f[i][0]>f[x][0] f[i][1]>f[x][1](当i为x的祖先)
并且有f[i][1]>f[i][0] 那么整个树的最大值为f[root][1]
如果不是记忆化会爆一个点。
如果加了优化速度也会快很多。
当然也可以使用DP,但是写起来会麻烦一点,初始化什么的比较难处理。
#include<stdio.h>
#include<iostream>
#include<vector>
#include<memory.h>
using namespace std;
const int MAX_N = 6001;
const int MAX_M = 2;
const int INF = -MAX_N*MAX_N;
vector<int> v[MAX_N];
int N;
int f[MAX_N][MAX_M];
bool visited [MAX_N][MAX_M];
int w[MAX_N];
bool isroot[MAX_N];
int root;
int ans = INF;
int find_root()
{
int i;
for (i=1;i<=N;i++)
if (isroot[i]) return i;
return -MAX_M;
}
int init()
{
int i;
int K,L;
memset(isroot,true,sizeof(isroot));
scanf("%d",&N);
for (i=1;i<=N;i++)
scanf("%d",&w[i]);
for (i=1;i<=N;i++)
scanf("%d %d",&K,&L),v[L].push_back(K),isroot[K]=false;
root=find_root();
memset(visited,false,sizeof(visited));
}
int work(int t,bool treasure)
{
if (!v[t].size()&&treasure) return f[t][treasure]=max(0,w[t]);
if (!v[t].size()&&!treasure) return f[t][treasure]=0;
if (visited[t][treasure]) return f[t][treasure];
visited[t][treasure] = true ;
int i;
int tmp;
f[t][treasure]=INF;
if (treasure)
{
tmp = max(0,w[t]);
for (i=0;i<v[t].size();i++)
tmp +=max(work(v[t][i],false),0);
f[t][treasure] = tmp ;
}
{
tmp = 0;
for (i=0;i<v[t].size();i++)
tmp+=max(work(v[t][i],true),0) ;
}
//printf("f(%d,%d):%d\n",t,treasure,tmp);
return f[t][treasure] = max(f[t][treasure],tmp) ;
}
int put()
{
ans=max(f[root][true],f[root][false]);
printf("%d",ans);
}
int main()
{
init();
work(root,true);
work(root,false);
put();
return 0;
}