树形dp初次学习

1、什么是树型动态规划
顾名思义,树型动态规划就是在“树”的数据结构上的动态规划,平时作的动态规划都是线性的或者是建立在图上的,线性的动态规划有二种方向既向前和向后,相应的线性的动态规划有二种方法既顺推与逆推,而树型动态规划是建立在树上的,所以也相应的有二个方向:

1、叶->根:在回溯的时候从叶子节点往上更新信息

2、根 - >叶:往往是在从叶往根dfs一遍之后(相当于预处理),再重新往下获取最后的答案。

不管是 从叶->根 还是 从 根 - >叶,两者都是根据需要采用,没有好坏高低之分。

2、树形dp的理解
DP的关键就在于状态的定义以及找转移
首先要考虑清楚状态,状态要能够很好地并且完整地描述子问题
其次考虑最底层的状态,这些状态一般是最简单的情况或者是边界情况
再就是考虑某一个状态能从哪些子状态转移过来,同时还要考虑转移的顺序,确保子问题已经解决
树形DP很多时候就是通过子节点推父亲节点的状态

3、题目解析
poj2342
一棵树,每个节点有权值,儿子与父亲不能同时取,求解从树上选取点能获得的最大权值

dp[i][0]表示不取,dp[i][1]表示取。

设j为i的儿子节点,dp[i][0] += max(dp[j][0], dp[j][1]), dp[i][1] += dp[j][0];

入度为零的点是根,从根开始进行深搜。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int N = 6 * 1e3 + 5;

int dp[N][2];
vector<int>ve[N];
int in[N];

void dfs(int u)
{
    int len = ve[u].size();
    for(int i = 0;i < len;++i)
    {
        int v = ve[u][i];
        dfs(v);
        dp[u][1] += dp[v][0];
        dp[u][0] += max(dp[v][0],dp[v][1]);
    }
}

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof(dp));
        memset(in,0,sizeof(in));
        for(int i = 1;i <= n;++i)
            ve[i].clear();
        for(int i = 1;i <= n;++i)
        {
            scanf("%d",&dp[i][1]);
        }
        int u,v;
        while(~scanf("%d %d",&u,&v))
        {
            if(u == 0 && v == 0)
                break;
            ve[v].push_back(u);
            in[u]++;
        }
        int root;
        for(int i = 1;i <= n;++i)
        {
            if(in[i] == 0)
            {
                root = i;
                break;
            }
        }
        dfs(root);
        int MAX = -1;
        for(int i = 1;i <= n;++i)
        {
            MAX = max(MAX,dp[i][0]);
            MAX = max(MAX,dp[i][1]);
        }
        printf("%d\n",MAX);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36386435/article/details/82155549