树型DP

18 篇文章 1 订阅
15 篇文章 7 订阅

概念

树型DP即在树上进行DP。树是无环图,顺序可以是从叶子到根节点,也可以从根到叶子节点。一般树型DP的特征很明显,即状态可以表示为树中的节点,每个节点的状态可以由其子节点状态转移而来(从叶子到根的顺序),或是由其父亲节点转移而来(从根到叶节点的顺序),也可是两者结合。找出状态和状态转移方程仍然是树型DP的关键。


例题

没有上司的晚会

题目描述

Ural大学有N个职员,编号为1~N。他们有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。每个职员有一个快乐指数。现在有个周年庆宴会,要求与会职员的快乐指数最大。但是,没有职员愿和直接上司一起参加宴会。

输入

第一行一个整数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

样例输出

5

分析

题意是从N个人中选出若干人,使得其快乐指数最大。但是选人有一些限制条件,即不允许上下级同时出现。
N个人的从属构成一棵树。设f[i][0]表示i不参加晚会,以i为根的子树能达到的最大快乐指数;f[i][1]表示i参加晚会其所在的子树能达到的最大的快乐指数。
则目标状态为max(f[root][0],f[root][1])
状态转移方程f[i][0]=sum(max(f[j][1],f[j][0])) j为i的儿子
f[i][1]=sum([f[j][0])+happy[i] j为i的儿子。
初识状态即叶子节点的状态。f[leaf][0]=0,f[leaf][1]=happy[leaf]。

源代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,x,y,root;
bool v[6001],b[6001];
int father[6001],f[6001][2];
void get(int &p){
    int flag=1;char ch;
    for(p=0;ch>'9'||ch<'0';ch=getchar())
        if(ch=='-')
            flag=-1;
    for(;ch<='9'&&ch>='0';ch=getchar())
        p=p*10+ch-'0';
    p*=flag;
}
void dp(int i){
    v[i]=1;
    for(int j=1;j<=n;j++)
        if(!v[j]&&father[j]==i){
            dp(j);
            f[i][1]+=f[j][0];
            f[i][0]+=max(f[j][1],f[j][0]);
        }
}
int main()
{
    get(n);
    for(int i=1;i<=n;i++)
        get(f[i][1]);
    for(int i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        father[x]=y;b[x]=1;
    }
    for(int i=1;i<n;i++)
        if(!b[i]){
            root=i;
            break;
        }
    dp(root);
    printf("%d",max(f[root][0],f[root][1]));
}

二叉苹果树

题目描述

有一棵苹果树,如果树枝有分叉,一定是分 2 叉(就是说没有只有 1 个儿子的结点)。这棵树共有 N 个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是 1。 我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有 4 个树枝的树:

现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。 给定需要保留的树枝数量,求出最多能留住多少苹果。

输入

第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

样例输出

21

分析

将枝条上的苹果移到靠近叶子节点的一端的节点上,更好理解。
设f[i][j]表示以i为根的子树保留j个节点所能保留的最大苹果数。
则f[i][j]=max(f[i.leftson][k]+f[i.rightson][j-1-k])+a[i] (0=

源代码
#include<cstdio>
#include<iostream>
using namespace std;
struct node{int l,r,v;}tree[1001];
int flag[101],f[101][101];
int n,q,root;
void get(int &p){
    int flag=1;char ch;
    for(p=0;ch>'9'||ch<'0';ch=getchar())if(ch=='-') flag=-1;
    for(;ch<='9'&&ch>='0';ch=getchar())p=p*10+ch-'0';
    p*=flag;
}
void dfs(int i,int j){
    int k;
    if(!j)f[i][j]=0;
    else if(!tree[i].r&&!tree[i].l) f[i][j]=tree[i].v;
    else{
        f[i][j]=0;
        for(k=0;k<j;k++){
            if(!f[tree[i].l][k])dfs(tree[i].l,k);
            if(!f[tree[i].r][j-k-1])dfs(tree[i].r,j-k-1);
            f[i][j]=max(f[i][j],f[tree[i].l][k]+f[tree[i].r][j-k-1]+tree[i].v);
        }
    }
}
int main()
{
    get(n);get(q);
    for(int i=1;i<n;i++){
        bool p=0;int x,y,m;
        get(x);get(y);get(m);
        for(int j=1;j<=n;j++)
            if(tree[j].l==y||tree[j].r==y)p=1;
        if(!p){
            if(!tree[x].l)tree[x].l=y;
            else if(!tree[x].r)tree[x].r=y;
            tree[y].v=m;flag[y]=1;
        }
        else{
            if(!tree[y].l)tree[y].l=x;
            else if(!tree[y].r)tree[y].r=x;
            tree[x].v=m;flag[x]=1;
        }
    }
    for(int i=1;i<=n;i++)
        if(!flag[i]){root=i;break;}
    dfs(root,q+1);
    printf("%d",f[root][q+1]);
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值