树形依赖的背包 树形dp

树形DP有一个独特的优化,就是通过递归,枚举目前有效的元素个数,求dp[ i ][ j ] (表示 选取以i为根的子树中有选取j个元素的最大取值)

(搭配 siz 数组表示当前该节点的总共子孙数)

1.hdu1561(树形依赖背包裸题)

注意 siz 数组的运用,以及 u 点选择的节点数时要逆向枚举,就像01背包

复杂度看似O(n^3),实际是 O( n^2 ) 左右。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

const int maxn = 250;
vector<int> g[maxn];
int dp[maxn][maxn];
int val[maxn];
int siz[maxn];
int n,m;

//dp[i][j] 表示 选取以i为根的子树中有选取j个元素的最大取值
void dfs(int u){
    siz[u]=1;
    dp[u][1] = val[u];
    for(int i=0; i<g[u].size(); i++){
        int v = g[u][i];
        dfs(v);                                //这里的siz[u]不包括siz[v] ,并且是把效率很低的2^n举法用01背包来做
        for(int i=siz[u]; i>=1; i--){         //这里就像01背包里,避免由这个点的情况递推这个点的更佳情况
            for(int j=1; j<=siz[v]&&i+j<=m; j++){  //就比如要避免刚刚还说是从v取3个点推出的最优
                dp[u][i+j] = max(dp[u][i+j], dp[u][i]+dp[v][j]); //后面又从前面的dp值而只从j中取1个点得出错误的更优解
            }
        }
        siz[u] += siz[v];
    }
}

int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        if(n==0&&m==0) break;
        for(int i=0; i<=n; i++){
            for(int j=0; j<=n; j++){
                dp[i][j] = 0;
            }
            g[i].clear();
        }
        int t;
        for(int i=1; i<=n; i++){
            scanf("%d%d",&t,val+i);
            g[t].push_back(i);
        }
        m++;
        dfs(0);
        printf("%d\n", dp[0][m]);
    }
}

2.codeforces 815C  (树形dp)

这个选取树上物品可以不需要有父子关系的,但使用优惠券和父子关系有关,所以可以把 dp数组多增加一维,表示是否能够使用优惠券。

只需要设置默认值为 inf ,再这样初始化:

dp[u][0][0]=0;

dp[u][1][0]=c[u];

dp[u][1][1]=c[u]-d[u];

就可以在枚举时考虑到 0 这个元素。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;

const int maxn = 5005;
int dp[maxn][maxn][2];    //dp[i][j]表示以i为根的子树中取j个元素的最大值
vector<int> g[maxn];      //再来一维表示是否购买根节点i这个元素,也就是用不用优惠券
int val[maxn],d[maxn];
int siz[maxn];

void dfs(int u){
    siz[u] = 1;
    dp[u][1][1] = val[u]-d[u];
    dp[u][1][0] = val[u];
    dp[u][0][0] = 0;
    for(int i=0; i<g[u].size(); i++){
        int v=g[u][i];
        dfs(v);      //这里的siz[u]不包括siz[v]
        for(int i=siz[u]; i>=0; i--){    //这里的0是为了处理可以不取
            for(int j=0; j<=siz[v]; j++){
                dp[u][i+j][0] = min(dp[u][i+j][0], dp[u][i][0]+dp[v][j][0]);
                dp[u][i+j][1] = min(dp[u][i+j][1], dp[u][i][1]+min(dp[v][j][0],dp[v][j][1]));
            }
        }
        siz[u] += siz[v];
    }
}

int main(){
    int n,b;
    scanf("%d%d",&n,&b);
    scanf("%d%d",val+1,d+1);
    for(int i=2; i<=n; i++){
        int t;
        scanf("%d%d%d",val+i,d+i,&t);
        g[t].push_back(i);
    }
    memset(dp,0x3f,sizeof(dp));
    dfs(1);
    int ans=n;
    while(dp[1][ans][1]>b&&dp[1][ans][0]>b){
        ans--;
        //printf("%d %d\n",dp[1][ans][1],dp[1][ans][0] );
    }
    printf("%d\n", ans);
}

原文地址:https://www.cnblogs.com/-Zzz-/p/11674985.html

时间: 2024-10-08 20:20:12

树形依赖的背包 树形dp的相关文章

hdu 1011 Starship Troopers (依赖背包 树形dp)

题目: 链接:点击打开链接 题意: n个房间组成一棵树,你有m个战队,从1号房间开始依次clear每个房间,在每个房间需要花费的战队个数是bugs/20,得到的价值是the possibility of capturing a brain,求最大的价值. 算法: 树形dp,有依赖的背包问题.(依次clear每个房间) 思路: 状态转移dp[i][j]表示根结点为i时(房间i)花费j个战队能够得到的最大价值(捕捉到一个brain最大的可能值).递归求出每个根结点处的最大值,最后dp[1][m]就是

C. Coin Troubles 有依赖的背包 + 完全背包变形

http://codeforces.com/problemset/problem/283/C 一开始的时候,看着样例不懂,为什么5 * a1 + a3不行呢?也是17啊 原来是,题目要求硬币数目a3 > a4 > a2,那么,不选的话,是不合法的.就是0.0.0这样是不合法的,因为a3 = a4了. 那么就可以知道, a3起码都要选两个了. 那么怎么维护这个关系呢?,思路是有依赖的背包,需要a3的数目比a4的多,就可以把a4那件物品变成a3 + a4 那么每一次选择的时候,就隐含地选了一件a3

php 系统树形类,以树形方式显示

<?php/***系统树形类,以树形方式显示***/header("Content-type: text/html; charset=utf-8;");class TreeModel{    /**    *树形标识    */    public $icon = array('│','├','└');    public $nbsp = "   ";    /**    *树形的二维数组数据    */    public $arr = array();  

hdu 1561 The more, The Better (依赖背包 树形dp)

题目: 链接:点击打开链接 题意: 非常明显的依赖背包. 思路: dp[i][j]表示以i为根结点时攻击j个城堡得到的最大值.(以i为根的子树选择j个点所能达到的最优值) dp[root][j] = max(dp[root][j],dp[root][k]+dp[u][j-k]); u递归根结点,root当前根结点,每一个城堡之间的依赖关系形成森林.应该转化为树.再树形dp.仅仅需添加一个根结点即可.m++. 代码: #include<iostream> #include<cstdio&g

HAOI2010 软件安装 有依赖的背包DP

题目描述 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一 些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大). 但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j).幸运的 是,一个软件最多依赖另外一个软件.如果一个软件不能正常工作,那么它能够发挥的作用为0. 我们现在知道了软件之间的依赖关系:软件i依赖软件Di.现在请你设计出

动态规划-树形DP-树上背包专题

先看道题:选课 题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习. 现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b).一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少? 输入输出格式 输入格式: 第一行有两个整数N,M用空格隔开.(1<=N<=300,1<=M<=300) 接下来的N行

有依赖的背包

问题描述: 解法: 这种树形的结构我们首先很容易去想到 树形dp ,但是和 树形dp 不一样的地方在于它选一个节点的话还有附加条件(也就是依赖关系) 我们对于每一个节点可以把它看成一个分组背包 dp[i][j] 代表 选第 i 个节点 背包容量为 j 的时候 背包的最大价值 我们可以先采取 树形dp 的方法去更新叶子节点,然后更新父亲节点 更新父亲节点的状态转移方程也很好想: dp[i][j] = max(dp[i][j],dp[son][k]+dp[i][j-k]) int n,m; int

Consumer (有依赖的背包)

题目大意: 给你一些物品,每个物品有自己的花费和价值,每个物品 都有相对应的箱子, 如果你想要买这个物品,你就需要先买这个箱子(依赖),求 n个箱子花费 t 能获得的最大价值 思路: dp[i][j]  代表在 i 个箱子花费 j 能获得的最大价值 我们需要优先对箱子进行处理,处理完之后其他的都和 01背包一样了 具体的看代码注释吧 int dp[55][100010]; int n,t,box,num; int main () { int i,j,k; while(scanf("%d %d&q

poj3624 01背包入门 dp+滚动数组

poj3624 01背包 dp+滚动数组 Charm Bracelet Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 25458   Accepted: 11455 Description Bessie has gone to the mall's jewelry store and spies a charm bracelet. Of course, she'd like to fill it with the bes