树上的动规

树形dp,在没有写过之前还以为十分的难,其实掌握了基本的几种之后还是比较容易的。

还是回到做dp最难的一个步骤,如何定义状态?

我们对于一颗树,无非考虑的问题有几个,当前的根是谁?它的左右孩子如何处理?我们探讨一下这个几个问题。

我将用题目来一起代入,算是树形dp入门题了。

先来一道普适性最高的题目

洛谷P2015 二叉苹果树

首先我们定义F[ x ][ i ]表示当前为x 作为根节点,在这个根节点下面留住 i 条树枝 的所能留住的最多苹果的数量,则答案就是F[ 1 ][ m ].

那么我们怎么实现呢?

对于当前点,我们肯定是要知道子节点的最佳状态是多少才能做出抉择,那么我们只能递归这颗树,自下往上进行操作。

对于叶子节点,F 值就为0;那么对于一个不为叶子节点,有孩子的点怎么做出抉择呢?

我们考虑要在递归的时候算出每一个节点下面有多少个孩子,然后进行枚举考虑要留住多少个孩子,当然如果孩子数目小于要留住的m个枝条数目,那么只用枚举到孩子的数目;同理,留住的m个枝条数目大于孩子的数目。

于是我们可以写出       F[ x ][ i ]=max(F [ x ] [ i ],F[ x ][ i-j-1 ]+F[ u ][ j ]+value[k]);   (u 是当前根节点x的子节点,F[ u ][ j ]表示子节点u留 j 条边的最大值,那么F[ x ][ i ]表示当前根节点留 i 条边的最大值,i>j)

#include <bits/stdc++.h>
#define maxn 1005
using namespace std;
int tot,next[maxn],first[105],zhi[maxn],value[maxn],x,y,v,n,m,i,f[105][maxn],size[105]; 
void build(int x,int y,int v)
{
    tot++;
    next[tot]=first[x];
    first[x]=tot;
    zhi[tot]=y;
    value[tot]=v;
}
void dfs(int x,int father)
{
    int k,i,j,u;
    k=first[x];
    while (k!=-1)
    {
        u=zhi[k];
        if (u==father) 
        {
            k=next[k];
            continue;
        }
        dfs(u,x);
        size[x]+=size[u]+1;
        for (i=min(size[x],m);i>=0;i--)
            for (j=min(size[u],i-1);j>=0;j--)
                f[x][i]=max(f[x][i],f[x][i-j-1]+f[u][j]+value[k]);
        k=next[k];
    }
}
int main()
{
    cin>>n>>m;
    memset(first,-1,sizeof(first));
    for (i=1;i<=n-1;i++) 
    {
        cin>>x>>y>>v;
        build(x,y,v);
        build(y,x,v);
    }
    dfs(1,0);
    cout<<f[1][m]<<endl;
    return 0;
 } 
View Code

洛谷P1352 没有上司的舞会

这道就比较简单了

f[x][0]表示以x为根的子树,且x不参加舞会的最大快乐值

f[x][1]表示以x为根的子树,且x参加了舞会的最大快乐值

需要注意的是 上司参加了舞会,下属一定不能参加;而上司不参加舞会,下属不是一定要参加舞会,因为数据有负值出现

 状态转移方程:       f[x][1]+=f[u][0];
               f[x][0]+=max(f[u][0],f[u][1]);

#include <bits/stdc++.h>
#define maxn 6005
using namespace std;
int tot,next[maxn*10],first[maxn],zhi[maxn*10],rong[maxn],i,n,f[maxn][2],l,k;
void build (int x,int y)
{
    tot++;
    next[tot]=first[x];
    first[x]=tot;
    zhi[tot]=y;
    rong[y]++;
}
void dfs(int x)
{
    int k,u;
    k=first[x];
    while (k!=-1)
    {
        u=zhi[k];
        dfs(u);
        f[x][1]+=f[u][0];
        f[x][0]+=max(f[u][0],f[u][1]);
        k=next[k];
    }
}
int main()
{
    scanf("%d",&n);
    memset(first,-1,sizeof(first));
    for (i=1;i<=n;i++) scanf("%d",&f[i][1]);
    for (i=1;i<=n;i++) 
    {
        scanf("%d%d",&l,&k);
        if (l!=0) build(k,l);
    }
    for (i=1;i<=n;i++)
        if (rong[i]==0) 
        {
            dfs(i);
            cout<<max(f[i][0],f[i][1]);
        }
    return 0;
}
View Code

这两道算是最基本的树形dp模型了,我在网上看到还有高级的树形dp,比如换根dp等等,但是难度有点大,故没有涉及。

猜你喜欢

转载自www.cnblogs.com/Y-Knightqin/p/12322289.html