首先先推荐dsy的《树形动规》
基本步骤:
第一步:确定状态
用f[i][0]表示不选择i点时,i点及其子树能选出的最多的人数,
f[i][1]表示选择i点时,i点及其子树的最多人数
第二步:确定状态转移方程
f[i][0]= ∑(max(fa[j][0],f[j][1])) // j为i的儿子,以下都是
f[i][1] = 1+∑f[j][0]
边界:f[i][0]=0,f[i][1]=1 - - -i是叶节点
结果为max(f[root][0],f[root][1])
第三步:确定编程方式
void dfs(long long x)
{
v[x]=1;
for(long long i=1;i<=n;i++)
{
if(!v[i]&&(fa[i]==x))
{
dfs(i);
f[x][0]+=max(f[i][1],f[i][0]);
f[x][1]+=f[i][0];
}
}
}
P1352 没有上司的舞会
题目描述
某大学有N个职员,编号为1~N。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。
输入输出格式
输入格式:
第一行一个整数N。(1<=N<=6000)
接下来N行,第i+1行表示i号职员的快乐指数Ri。(-128<=Ri<=127)
接下来N-1行,每行输入一对整数L,K。表示K是L的直接上司。
最后一行输入0 0
输出格式:
输出最大的快乐指数。
输入输出样例
输入样例#1:
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0
输出样例#1:
5
分析:与基本步骤相似
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 6010;
int n;
int fa[N];
int vis[N];
long long dp[N][5];//dp[i][0]表示不选i时,i点及其子树能选出的最大值,dp[i][1]表示选i的时,i点及其子树能选出的最大值
void dfs(int x)
{
vis[x]=1;
int i;
for(i=1;i<=n;i++)
{
if(!vis[i]&&(fa[i]==x))
{
dfs(i);
dp[x][0]+=max(dp[i][0],dp[i][1]);
dp[x][1]+=dp[i][0];
}
}
}
int main()
{
int x, y;
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
cin >> n;
for(int i=1;i<=n;i++)
{
fa[i]=i;
dp[i][0]=0;
}
for(int i=1;i<=n;i++)
cin >> dp[i][1];
while(cin>>x>>y)
{
if(x==0&&y==0)
break;
fa[x]=y;
}
int ff;
for(int i=1;i<=n;i++)
{
if(fa[i]==i)
{
ff = i;
dfs(i);
break;
}
}
cout << max(dp[ff][0],dp[ff][1]) <<endl;
return 0;
}
2 . P2015 二叉苹果树
题目描述
有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)
这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。
我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树
2 5
\ /
3 4
\ /
1
现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。
给定需要保留的树枝数量,求出最多能留住多少苹果。
输入输出格式
输入格式:
第1行2个数,N和Q(1<=Q<= N,1<N<=100)。
N表示树的结点数,Q表示要保留的树枝数量。接下来N-1行描述树枝的信息。
每行3个整数,前两个是它连接的结点的编号。第3个数是这根树枝上苹果的数量。
每根树枝上的苹果不超过30000个。
输出格式:
一个数,最多能留住的苹果的数量。
输入输出样例
输入样例#1:
5 2
1 3 1
1 4 10
2 3 20
3 5 20
输出样例#1:
21
dp[i][j];//以i为根的有j个树枝的最大苹果数
如果i要选用它的儿子的苹果,那么就必须加上i和j这个树枝上的苹果(e[i][j])
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n, q;
int e[110][110];
int dp[110][110];//以i为根的有j个树枝的最大苹果数
int vis[110];
void dfs(int x)
{
vis[x]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i]&&e[i][x]!=-1)
{
dfs(i);
for(int j=q;j>=1;j--) //这里如果j从1遍历到q,那么由于dp[x][j]=max(dp[x][j],dp[x][k]+dp[i][j-k-1]+e[i][x]);dp[x][j]的值会受dp[x][小于j]的影响,而此时dp[x][小于j]有可能已经包含i的树枝了,这样就会有所重叠,
{
for(int k=0;k<j;k++)
{
dp[x][j]=max(dp[x][j],dp[x][k]+dp[i][j-k-1]+e[i][x]);
}
}
}
}
}
int main()
{
memset(e,-1,sizeof(e));
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
int a, b, w;
cin >> n >> q;
for(int i=1;i<n;i++)
{
cin >> a >> b >> w;
e[a][b]=e[b][a]=w;
}
dfs(1);
cout << dp[1