题目链接:传送门

题目大意:略。。

题目思路:其实单就这道题来说,一个主件最多两个附件,且附件不再包含附件,所以很简单,但是如果主件的附件无限制,附件也可包含无限制的附件,应该怎么做?

     首先推荐一篇论文  徐持衡的《浅谈几类背包题》,里面有详细的讲解,这一类树形依赖背包问题最终都能优化到  n*c 复杂度,n为物品数,c为背包容量

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <cctype>
#include <queue>
#include <string>
#include <vector>
#include<functional>
#include <set>
#include <map>
#include <climits>
#define lson root<<1,l,mid
#define rson root<<1|1,mid+1,r
#define fi first
#define se second
#define ping(x,y) ((x-y)*(x-y))
#define mst(x,y) memset(x,y,sizeof(x))
#define mcp(x,y) memcpy(x,y,sizeof(y))
using namespace std;
#define gamma 0.5772156649015328606065120
#define MOD 1000000007
#define inf 0x3f3f3f3f
#define N 1000005
#define maxn 32005
typedef pair<int,int> PII;
typedef long long LL; int n,m,x;
vector<int>V[];
int dp[][maxn];
int w[],v[];
void dfs(int u,int m){
if(m<=)return;
for(int e:V[u]){
for(int k=;k<=m;++k)dp[e][k]=dp[u][k];
dfs(e,m-w[e]);
for(int k=w[e];k<=m;++k)dp[u][k]=max(dp[u][k],dp[e][k-w[e]]+v[e]);
}
}
int main(){
int i,j,group,x,y;
while(scanf("%d%d",&m,&n)!=EOF){
mst(dp,);
for(i=;i<=n;++i)V[i].clear();
for(i=;i<=n;++i){
scanf("%d%d%d",&w[i],&v[i],&x);
v[i]*=w[i];
V[x].push_back(i);
}
dfs(,m);
printf("%d\n",dp[][m]);
}
return ;
}

然后再从这道题谈谈我对树形依赖背包的理解,不管怎么想怎么做,这是背包问题。

也就是对于一个物品,存在拿与不拿的情况,而如果不拿父节点,那么子节点也就只有不拿的情况。

加上树形结构以后,我们依然要保持背包的性质不变,也就是维护容量V包含价值最优的情况。

因为N*C^2复杂度有太多优秀的博客介绍,这里就不再赘述。

讲下个人对于 N*C  复杂度解法的理解。首先 二维数组 dp[u][V] ,u代表当前处理的节点u, V代表当前处理的容量(V<=背包总容量),整个 dp[u][V]保存的是处理到节点u容量V时的最优价值(处理到u,代表u节点左边所有的节点都已处理完毕)以及u所有子节点容量为V的最优值。

我们在处理节点时,存在拿与不拿的情况,而想选子节点则必须拿。那么假设处理到了节点x,我们可以先强制拿节点 x,这样就可以很方便的处理x的子节点,递归下去,对于子节点也同样处理,这样递归到底层时,我们把能拿的子节点都拿完了,但是这不一定是最优解,所以回溯的时候,则需要对拿与不拿进行取舍,因为是从子节点回溯,并且我们递归的时候是强制拿父节点的,因此可以直接选择 拿不拿子节点,而不用管父节点的限制,这样回溯维护最优值。

vector<int>V[];
int dp[][maxn];
int w[],v[];
void dfs(int u,int m){ ///u 父节点,m 当前可用容量(m<总容量)
if(m<=0)return; ///没有可用容量,背包装满了。。
for(int e:V[u]){ /// e 子节点
for(int k=;k<=m-w[e];++k)dp[e][k]=dp[u][k]; /// * 1 *
dfs(e,m-w[e]); ///强制拿e,方便处理e子节点 * 2 *
for(int k=w[e];k<=m;++k)dp[u][k]=max(dp[u][k],dp[e][k-w[e]]+v[e]);
///回溯更新 拿e后的最优值 与 不拿e ,保存两者最优值
}
}

在分析 * 1 *与 * 2 *;

* 1 *:和学长讨论了下是否有必要加上这个赋值循环?当然需要。对于节点e来说,强制拿e是在处理完e之前所有节点的基础上进行的,也就是在之前节点最优值的基础上进行的。

     否则对于 e 来说,它与整个树的关系脱离,它最后得到的值与与父节点保存的值没有比较的意义。

* 2 *:m-w[e]就代表强制拿了e,因为背包中已经有部分体积被e 所占,e的价值只需回溯的时候加上进行更新即可。但是这里似乎没有体现出 强制拿 u?因为依赖背包题目一般给

   的是一片森林。我们为了方便处理,可以加上一个虚拟树根,重量价值都为0,这样调用的时候,直接dfs(虚拟树根,背包总容量)。

不过感觉* 1 *处还是没有解释好,希望能有大牛补充,有不对的地方也请指出,,,

noip2006 金明的预算的更多相关文章

  1. [codevs1155][KOJ0558][COJ0178][NOIP2006]金明的预算方案

    [codevs1155][KOJ0558][COJ0178][NOIP2006]金明的预算方案 试题描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴 ...

  2. Luogu 1064 金明的预算方案 / CJOJ 1352 [NOIP2006] 金明的预算方案(动态规划)

    Luogu 1064 金明的预算方案 / CJOJ 1352 [NOIP2006] 金明的预算方案(动态规划) Description 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己 ...

  3. NOIP2006 金明的预算方案

    1.             金明的预算方案 (budget.pas/c/cpp) [问题描述] 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴的是,妈 ...

  4. [LuoguP1064][Noip2006]金明的预算方案

    金明的预算方案(Link) 题目描述 现在有\(M\)个物品,每一个物品有一个钱数和重要度,并且有一个\(Q\),如果\(Q = 0\),那么该物件可以单独购买,当\(Q != 0\)时,表示若要购买 ...

  5. 【洛谷P1064】[NOIP2006] 金明的预算方案

    金明的预算方案 显然是个背包问题 把每个主件和它对应的附件放在一组,枚举每一组,有以下几种选法: 1.都不选 2.只选主件 3.一个主件+一个附件 4.一个主件+两个附件 于是就成了01背包.. #i ...

  6. NOIP2006金明的预算方案[DP 有依赖的背包问题]

    题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”.今 ...

  7. NOIP 2006 金明的预算方案

    洛谷 P1064 金明的预算方案 https://www.luogu.org/problem/P1064 JDOJ 1420: [NOIP2006]金明的预算方案 T2 https://neooj.c ...

  8. 「NOIP2006」「LuoguP1064」 金明的预算方案(分组背包

    题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NNN元钱就行” ...

  9. tyvj 1057 金明的预算方案 背包dp

    P1057 金明的预算方案 时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 NOIP2006 提高组 第二道 描述 金明今天很开心,家里购置的新房就要领钥匙了 ...

随机推荐

  1. android studio教学视频资源(点开即看)

    android studio教学视频资源(点开即看) 自从Google推出android studio之后.包含github在内的非常多第三方代码库项目很多其它的採用的android studio编译 ...

  2. Android逆向之旅---SO(ELF)文件格式详解(转)

    第一.前言 从今天开始我们正式开始Android的逆向之旅,关于逆向的相关知识,想必大家都不陌生了,逆向领域是一个充满挑战和神秘的领域.作为一名Android开发者,每个人都想去探索这个领域,因为一旦 ...

  3. Effective Java-第三章

    第3章 对于所有对都通用的方法 尽管Object是一个具体类,但是设计它主要是为了扩展,它所有的非final方法(equals,hashCode,toString,clone和finalize)都有明 ...

  4. JS检查浏览器类型和版本号

    先取得Navigator对象的userAgent属性的小写信息,之后依据正則表達式推断赋值. var Sys = {}; var ua = navigator.userAgent.toLowerCas ...

  5. .htaccess中的apache rewrite规则写法详解

    .htaccess中的apache rewrite写法: 1 RewriteEngine On 2 RewriteCond %{HTTP_HOST} ^(www\.)?xxx\.com$ 3 Rewr ...

  6. Powershell对象选择,排序和变量存储

    PowerShell基础教程(17)——对象的选择.排序和变量存储 可以使用 Select-Object cmdlet 来创建新的.自定义的 Windows PowerShell 对象,后者包含的属性 ...

  7. redis 优缺点 使用场景

    1. 使用redis有哪些好处? (1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1) (2) 支持丰富数据类型,支持string,li ...

  8. go 语言学习笔计之结构体

    go 语言中的结构体方法 结构体名称的大小写有着不同的意义: 小写表示不能被别的包访问 package main import "fmt" type Rect struct { w ...

  9. ssh密码登陆的原理,密码的公私钥和sshkey的不是一回事

    密码登录的原理: 密钥加密: 创建分发密钥.分发到服务端的ssh-copy-id的本质是拷贝公钥到对面服务器的,authorized_keys文件下就会多了一个密钥文件信息.就会被拷贝入下面. 客户端 ...

  10. k8s入门

    一个目标:容器操作:两地三中心:四层服务发现:五种Pod共享资源:六个CNI常用插件:七层负载均衡:八种隔离维度:九个网络模型原则:十类IP地址:百级产品线:千级物理机:万级容器:相如无亿,K8s有亿 ...