关于树形DP的工作报告

最近做了很多树形DP的题呼,来总结一下.

希望以此增加全世界人民对树形DP的了解.

大概就是3种题型:

树上取点集的最值

代表题P1352 没有上司的舞会

每一个节点会影响与他相关的节点,所以说决策就很简单了,这个节点选或不选.

在选或不选的情况下,再加上对相关节点的影响.

状态转移方程如下:


dp[x][0]+=max(dp[y][0],dp[y][1]);
dp[x][1]+=dp[y][0];

在转移之前,先要向下递归他的儿子.

P1352的代码:


#include<bits/stdc++.h>

using namespace std;

vector<int>fas[10010];

int n,root;
int happy[10010],dp[10010][2];
bool head[10010];

void dg(int);

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",happy+i);
    }
    for(int i=1;i<=n-1;i++)
    {
        int father,son;
        scanf("%d%d",&son,&father);
        fas[father].push_back(son);
        head[son]=true;
    }
    int a,b;
    scanf("%d%d",&a,&b);
    for(int i=1;i<=n;i++)
    {
        if(head[i]==false)
        {
            root=i;
            break;
        }
    }
    dg(root);
    printf("%d",max(dp[root][1],dp[root][0]));
return 0;
}

void dg(int x)
{
    dp[x][0]=0;
    dp[x][1]=happy[x];
    for(int i=0;i<fas[x].size();i++)
    {
        int y=fas[x][i];
        dg(y);
        dp[x][0]+=max(dp[y][0],dp[y][1]);
        dp[x][1]+=dp[y][0];
    }
}

树上背包

代表题P2015 二叉苹果树

P2014也很经典,只不过我是用多叉转二叉来写的.

其实便是加上的点的数量限制的上一种题型.

上一种是直接做出抉择就好了,这一种就要加上一个背包,来做出当前节点下能用最大背包容量获得的最值.

背包的过程如下:


for(int j=q;j>=1;j--)
            for(int k=j-1;k>=0;k--)
                dp[root][j]=max(dp[root][j],apple[root][to]+dp[root][j-k-1]+dp[to][k]);

P2015的代码:


#include<bits/stdc++.h>

using namespace std;

vector<int>tree[110];

int n,q;
int apple[110][110],dp[110][110];
bool falg[110];

void dfs(int);

int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n-1;i++)
    {
        int a,b,val;
        scanf("%d%d%d",&a,&b,&val);
        tree[a].push_back(b);
        tree[b].push_back(a);
        apple[a][b]=val;
        apple[b][a]=val;
    }
    dfs(1);
    printf("%d",dp[1][q]);
return 0;
}

void dfs(int root)
{
    falg[root]=true;
    for(int i=0;i<tree[root].size();i++)
    {
        int to=tree[root][i];
        if(falg[to]==true)continue;
        falg[to]=true;
        dfs(to);
        for(int j=q;j>=1;j--)
            for(int k=j-1;k>=0;k--)
                dp[root][j]=max(dp[root][j],apple[root][to]+dp[root][j-k-1]+dp[to][k]);
    }
}

子树的最值

代表题P1122 最大子树和

这类题一般是没有固定的根节点的,因为可以子树可以以任意一个节点作为根.

而且儿子与父亲的界限一般不明确(因为给出的是连接节点的边),要用前向星双向建边.

加个标记防止往回走罢.

DP数组代表着以当前节点为根的子树的最值.

DP数组附初值为节点的权值.

每一个节点向下递归,然后回溯时该节点累加上子节点.

子节点的DP值小于0(求最大值)或者大于0(求最小值)的话就不累加了.

P1122的代码:


#include<bits/stdc++.h>

using namespace std;

vector<int>tree[16010];

int n,ans=-2147483647;
int num[16010],dp[16010];

void dfs(int,int);

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",num+i);
    for(int i=1;i<=n-1;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        tree[a].push_back(b);
        tree[b].push_back(a);
    }
    dfs(1,0);
    printf("%d",ans);
return 0;
}

void dfs(int root,int hisfather)
{
    dp[root]=num[root];
    for(int i=0;i<tree[root].size();i++)
    {
        int to=tree[root][i];
        if(to==hisfather)continue;
        dfs(to,root);
        dp[root]+=max(0,dp[to]);
    }
    ans=dp[root]>ans?dp[root]:ans;
}

我本人见过的树形DP也就上面几种,以后遇到别的还会补充.

转载于:https://www.cnblogs.com/Lemir3/p/11062974.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值