P1064 [NOIP 2006 提高组] 金明的预算方案——依赖背包
背景
弱化版
入题之前,先看看弱化版【开心的金明】
对于这道题,比平常所作的01背包多了一个重要度。
但仔细想想,背包问题主要是考虑价值与空间的比值(即性价比)。
只需将原物品价值乘以重要度即可。
d p [ j ] = m a x ( d p [ j ] , d p [ j − 价值 ] + 贡献 ) dp[j]=max(dp[j],dp[j−价值]+贡献) dp[j]=max(dp[j],dp[j−价值]+贡献)
弱化Code
Code ED:
//算法:01背包
//时间复杂度:O(n^2)
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
const int N = 5e5+9;struct Node{int t,v;
}g[N];
int n,m;
int dp[N];signed main(void){ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);cin >> n >> m;for(int i = 1; i <= m; ++i){cin >> g[i].t >> g[i].v;g[i].v = g[i].t*g[i].v;}for(int i = 1; i <= m; ++i){for(int j = n; j >= 0; --j){if(j >= g[i].t)dp[j] = max(dp[j], dp[j-g[i].t]+g[i].v);}}cout << dp[n] << "\n";return 0;
}
强化版
现在入题【金明的预算方案】
题目大意
又多了一对关系:主件与附件。
主件可以单独购买,附件需要购买相应的主件后才开放购买。
0对应主件,其它数字对应是附件所对应的主件编号(注意:别被题目样例中只有0和1误导,附件对应数字不为0但对应所对应的主件编号。因为我被这绕了十分钟)。
问题主要是考虑对主件与附件进行区分和计算。
所以这道题本质上仍是01背包,不过多了些依赖关系,即为依赖背包。
变量存储
考虑主件与附件的对应关系,需要对他们进行编号。
使用一结构体方便些。
定义id为其编号,v为其价值,s为其总贡献,f为对应编号值(也可视为判断主附件)。
当然,我们仍需记录各项元素原数值。
定义n,m,v,p,q;为总钱数、空间、原价值、重要度和编号值。
再定义f[N]记录编号值,动转数组dp[N]。
变量命名
struct Node{int id,v,s,f;//编号、价值、总贡献、主附件判断
}g[N];
int n,m,v,p,q;
int f[N],dp[N];
判断主附件
因为需要判断主附件,所以它们的各项数值需要分开来记。
之前定义的q就可以判断(为0为主件,其它为附件)
这里:
if(q == 0) g[i].id = i, g[i].v = v, g[i].s = v*p, g[i].f = 0;else g[i].id = q, g[i].v = v, g[i].s = v*p, g[i].f = ++f[q];
编号排序
编号嘛,自然要排好序才好做。
直接利用结构体id编号即可。
bool sort1(Node a, Node b){//编号排序sort1函数if(a.id == b.id) return a.f < b.f;return a.id < b.id;
}
样例
存储已经完毕,剩下的便是转移方程咯。
先手玩几把样例(必应之举)。
1000 5
800 2 0 买这个,还剩200块,贡献为800*2 = 1600
400 5 1 依赖1号物品
300 5 1 依赖1号物品
400 3 0 买这个,还剩600块,贡献为400*3 = 1200
500 2 0 买这个,还剩500块,贡献为500*2 = 1000
小贴士
(别把贡献当价格买,不然啥也买不起)
玩完#1样例后,发现买4、5号主件最值(1000+1200 = 2200)。
推理一下:那是不是存在部分数据,只选主件?
答案是肯定的。
购买情况
(不试你也行啊,DP肯定多情况啊,OI赛制是让你玩的?)
简单的动转方程: d p [ j ] = m a x ( d p [ j ] , d p [ j − 组合价格 ] + 组合价值 ) dp[j]=max(dp[j],dp[j−组合价格]+组合价值) dp[j]=max(dp[j],dp[j−组合价格]+组合价值)
所以综合题目分析来看,存在以下四种情况:
{ 只买主件 : m a x ( d p [ j ] , d p [ j − g [ i ] . v ] + g [ i ] . s ) 买主件和第一种附件 : m a x ( d p [ j ] , d p [ j − g [ i ] . v − g [ i + 1 ] . v ] + g [ i ] . s + g [ i + 1 ] . s ) 买主件和第二种附件 : m a x ( d p [ j ] , d p [ j − g [ i ] . v − g [ i + 2 ] . v ] + g [ i ] . s + g [ i + 2 ] . s ) 三件都买 : m a x ( d p [ j ] , d p [ j − g [ i ] . v − g [ i + 1 ] . v − g [ i + 2 ] . v ] + g [ i ] . s + g [ i + 1 ] . s + g [ i + 2 ] . s ) \begin{cases} 只买主件:max(dp[j], dp[j-g[i].v]+g[i].s)\\ 买主件和第一种附件:max(dp[j], dp[j-g[i].v-g[i+1].v]+g[i].s+g[i+1].s)\\ 买主件和第二种附件:max(dp[j], dp[j-g[i].v-g[i+2].v]+g[i].s+g[i+2].s)\\ 三件都买:max(dp[j], dp[j-g[i].v-g[i+1].v-g[i+2].v]+g[i].s+g[i+1].s+g[i+2].s) \end{cases} ⎩ ⎨ ⎧只买主件:max(dp[j],dp[j−g[i].v]+g[i].s)买主件和第一种附件:max(dp[j],dp[j−g[i].v−g[i+1].v]+g[i].s+g[i+1].s)买主件和第二种附件:max(dp[j],dp[j−g[i].v−g[i+2].v]+g[i].s+g[i+2].s)三件都买:max(dp[j],dp[j−g[i].v−g[i+1].v−g[i+2].v]+g[i].s+g[i+1].s+g[i+2].s)
强化Code
if条件得加附件编号对应主件编号,具体看代码吧。
Code ED:
//算法:依赖背包
//时间复杂度:O(n^2)
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
const int N = 4e5+10;struct Node{int id,v,s,f;//编号、价值、总贡献、主附件判断
}g[N];
int n,m,v,p,q;
int f[N],dp[N];bool sort1(Node a, Node b){//编号排序sort1函数if(a.id == b.id) return a.f < b.f;return a.id < b.id;
}signed main(void){ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);cin >> n >> m;for(int i = 1; i <= m; ++i){cin >> v >> p >> q;if(q == 0) g[i].id = i, g[i].v = v, g[i].s = v*p, g[i].f = 0;else g[i].id = q, g[i].v = v, g[i].s = v*p, g[i].f = ++f[q];}sort(g+1, g+m+1, sort1);//进行编号排序for(int i = 1; i <= m; ++i){if(g[i].f) continue;for(int j = n; j >= g[i].v; --j){dp[j] = max(dp[j], dp[j-g[i].v]+g[i].s);//记录原始最大值(即只买主件)if(g[i+1].id == g[i].id && j >= (g[i].v+g[i+1].v))//买第一个附件dp[j] = max(dp[j], dp[j-g[i].v-g[i+1].v]+g[i].s+g[i+1].s);if(g[i+2].id == g[i].id && j >= (g[i].v+g[i+2].v))//买第二个附件dp[j] = max(dp[j], dp[j-g[i].v-g[i+2].v]+g[i].s+g[i+2].s);if(g[i+2].id == g[i].id && j >= (g[i].v+g[i+1].v+g[i+2].v))//俩附件都买dp[j] = max(dp[j], dp[j-g[i].v-g[i+1].v-g[i+2].v]+g[i].s+g[i+1].s+g[i+2].s);//《g[i+1-g[i+2].v].v]》}}cout << dp[n] << "\n";return 0;
}
补充练习
【P1273 有线电视网】