树形dp小结

做了好久的树形DP(大雾) ,(noip)csp-s考了好多树形DP
树形DP基本分为这几种

1.最大独立集(没有上司的舞会)

经典树形DP
\(dp[i][0/1]\) 表示i这个点选与不选。
\(dp[u][0] += dp[v][1];\)
\(dp[u][1] += max(dp[v][0],dp[v][1]);\)

2.最小点覆盖(战略游戏)

状态与上面一样
\(dp[u][0] += dp[v][1];\)
\(dp[u][1] += min(dp[v][0],dp[v][1]);\)

3.最小支配集(SDOI保安站岗)

\(dp[i][0/1/2]\) 表示这个点被自己覆盖,被儿子覆盖,被父亲覆盖
\(dp[u][0] += min(dp[v][0],dp[v][1],dp[v][2]);\)
\(dp[u][1] += min(dp[v][1],dp[v][0]);\)
(注:如果u所有的儿子v的dp[v][1] < dp[v][0] 强制选一个最小的dp[v][0]);
\(dp[u][2] += min(dp[v][0],dp[v][1]);\)

   int dt=1e9+7,cnt=0;f[u][1]=pa[u];
    for(int p=last[u];p;p=pre[p])
    {
        int v=other[p];
        if(v==fa)continue;
        dfs(v,u);
    }
    for(int p=last[u];p;p=pre[p])
    {
        int v=other[p];
        if(v==fa)continue;
        f[u][1]+=min(min(f[v][2],f[v][1]),f[v][0]);
        f[u][2]+=min(f[v][1],f[v][0]);
        if(f[v][1]<f[v][0])cnt = 1;
        else dt=min(dt,f[v][1]-f[v][0]);
        f[u][0]+=min(f[v][1],f[v][0]);
    }
    if(cnt==0)
    f[u][0]+=dt;

4.Computer

求树上每个点到最远点的距离
\(dp[i][0/1/2]\) 分别表示子树内最长链,次长链,子树外最长链

inline void dfs1(int x,int fa)
{
    int fis = 0 ,sec = 0;
    for(int p = last[x];p;p = pre[p])
    {
        int v = other[p];
        if(v == fa)continue;
        dfs1(v,x);
        int tmp = dp[v][0] + len[p];
        if(tmp >= fis)
        sec = fis,fis = tmp;
        else if(tmp > sec)
        sec = tmp;
    }
    dp[x][0] = fis;dp[x][1] = sec;
}
inline void dfs2(int x,int fa)
{
    for(int p = last[x];p;p = pre[p])
    {
        int v = other[p];
        if(v == fa)continue;
        if(dp[v][0] == dp[x][0] - len[p])
        dp[v][2] = len[p] + max(dp[x][1],dp[x][2]);
        else
        dp[v][2] = len[p] + max(dp[x][2],dp[x][0]);
        dfs2(v,x);
    }
}

5.树上背包(选课,有线电视网)

\(dp[i][j]\) 表示子树i中选了j门课
$ dp[u][j] = max(dp[u][j],dp[v][j-k] + dp[u][k]) $

   for(int p=last[u];p;p=pre[p])
    {
        int v=other[p];
        for(int i=m+1;i>=1;i--)
        {
            for(int j=i;j>=1;j--)
            if(f[u][j]>=0&&f[v][i-j]>=0)
            f[u][i]=max(f[u][i],f[u][j]+f[v][i-j]);
        }
    }

复杂度\(O(nm^{2})\)
用dfn序优化之后就是\(O(nm)\)
树上背包基本都是这个套路
特别的像HAOI树上染色一题

     for(int i = min(siz[u],k) ; i >= 0 ;i--)
        {
            for(int j = 0; j <= min(siz[v],i);j++)
            {
                if(f[u][i-j] == -1)continue;
                ll val = (ll)(k-j)*j*len[p] + (ll)(siz[v]-j)*(n-k-siz[v]+j)*len[p];
                f[u][i] = max(f[u][i],f[v][j] + f[u][i-j] + val);
            }
        }

取min的两个位置要特别注意,如果不取min的话复杂度就是错的,这其中的道理不好分析,但这样做完之后,复杂度就是\(O(n^{2})\)

总结

树形DP多数分为\(dp[i][0/1]\),和背包几种类型,主要考虑子树中对当前点的贡献,或者子树外对当前点的贡献,来写出方程。

原文地址:https://www.cnblogs.com/wtz2333/p/12233115.html

时间: 2024-07-30 07:37:30

树形dp小结的相关文章

[poj2342]Anniversary party_树形dp

Anniversary party poj-2342 题目大意:没有上司的舞会原题. 注释:n<=6000,-127<=val<=128. 想法:其实就是最大点独立集.我们介绍树形dp 树形dp就是以节点或者及其子树为信息,进行动态规划.用dfs的原理,遍历,在回溯是更新父亲节点. 然后,关于这道题,我们就可以对于每一个节点进行标记,然后对于满足条件的节点进行遍历.设状态就是两个dp,分别表示选当前根节点和不选当前根节点.更新是瞎jb更新即可... .... 最后,附上丑陋的代码...

[bzoj1812][IOI2006]riv_多叉树转二叉树_树形dp

riv bzoj-1812 IOI-2006 题目大意:给定一棵n个点树,要求在上面建立k个收集站.点有点权,边有边权,整棵树的代价是每个点的点权乘以它和它的最近的祖先收集站的距离积的和. 注释:$1\le n \le 100$,$1\le k \le 50$. 想法:显然,这是一道树形dp题.状态也非常容易... ...只不过,我们好像要枚举子集... 所以,我们这里有一个黑科技:多叉树转二叉树. 我们先把它转成二叉树,然后再进行转移即可. 状态:dp[i][j][k]表示以i为根的子树中选出

[bzoj2657][Zjoi2012]旅游 journey_ 对偶图_树形dp

旅游 bzoj-2657 Zjoi-2012 题目大意:题目链接 注释:$1\le K\le 2\cdot 10^5$. 想法:这题... 感觉和上一个题的提示有些类似,就是题目生怕你不知道这是一道对偶图的题... ... 我们先把它转成对偶图.然后我们只把分割n变形的线段两侧的点之间连边,这样就是一棵树. 紧接着我们想要遍历最多的城市,其实就是找树上直径.树形dp即可. 最后,附上丑陋的代码... ... #include <iostream> #include <cstdio>

HDU-2196 Computer (树形DP)

最近在看树形DP,这题应该是树形DP的经典题了,写完以后还是有点感觉的.之后看了discuss可以用树分治来做,以后再试一试. 题目大意 找到带权树上离每个点的最远点.︿( ̄︶ ̄)︿ 题解: 对于每一个点的最远点,就是以这个点为根到所有叶子节点的最长距离.但是如果确定根的话,除了根节点外,只能找到每个节点(度数-1)个子树的最大值,剩下一个子树是该节点当前的父亲节点. 所以当前节点的最远点在当前节点子树的所有叶子节点以及父亲节点的最远点上(当父亲节点的最远点不在当前节点的子树上时), 如果父亲节

UVA-01220 Party at Hali-Bula (树形DP+map)

题目链接:https://vjudge.net/problem/UVA-1220 思路: 树形DP模板题,求最大人数很简单,难点在于如何判断最大人数的名单是否有不同的情况: 解决方法是用一个数组f[manx][2]记录该节点是否出场的情况,为真时代表有多种情况; 具体讨论: 当父节点的值加上某个子节点的值时,他的f的情况也和该子节点一样: 当某个节点dp(i, 0) == dp(i, 1), 则该节点以及它的父节点也一定有多种情况(父节点必定取其中之一). Code: 1 #include<bi

HDU 1520 树形dp裸题

1.HDU 1520  Anniversary party 2.总结:第一道树形dp,有点纠结 题意:公司聚会,员工与直接上司不能同时来,求最大权值和 #include<iostream> #include<cstring> #include<cmath> #include<queue> #include<algorithm> #include<cstdio> #define max(a,b) a>b?a:b using nam

HDU2196 Computer(树形DP)

和LightOJ1257一样,之前我用了树分治写了.其实原来这题是道经典的树形DP,感觉这个DP不简单.. dp[0][u]表示以u为根的子树中的结点与u的最远距离 dp[1][u]表示以u为根的子树中的结点与u的次远距离 这两个可以一遍dfs通过儿子结点转移得到.显然dp[0][u]就是u的一个可能的答案,即u往下走的最远距离,还缺一部分就是u往上走的最远距离: dp[2][u]表示u往上走的最远距离 对于这个的转移,分两种情况,是这样的: dp[2][v] = max( dp[0][u]+w

hdu5593--ZYB&#39;s Tree(树形dp)

问题描述 ZYB有一颗N个节点的树,现在他希望你对于每一个点,求出离每个点距离不超过KK的点的个数. 两个点(x,y)在树上的距离定义为两个点树上最短路径经过的边数, 为了节约读入和输出的时间,我们采用如下方式进行读入输出: 读入:读入两个数A,B,令fai??为节点i的父亲,fa?1??=0;fa?i??=(A∗i+B)%(i−1)+1,i∈[2,N] . 输出:输出时只需输出N个点的答案的xor和即可. 输入描述 第一行一个整数TT表示数据组数. 接下来每组数据: 一行四个正整数N,K,A,

CF 219D Choosing Capital for Treeland 树形DP 好题

一个国家,有n座城市,编号为1~n,有n-1条有向边 如果不考虑边的有向性,这n个城市刚好构成一棵树 现在国王要在这n个城市中选择一个作为首都 要求:从首都可以到达这个国家的任何一个城市(边是有向的) 所以一个城市作为首都,可能会有若干边需要改变方向 现在问,选择哪些城市作为首都,需要改变方向的边最少. 输出最少需要改变方向的边数 输出可以作为首都的编号 树形DP 先假定城市1作为首都 令tree(i)表示以i为根的子树 dp[i]表示在tree(i)中,若以i为首都的话,需要改变的边数 第一次