金明的预算方案【NOIP2006提高组】
分析
该问题属于带有依赖关系的背包问题,主件和附件的购买必须满足先决条件。每个主件最多有两个附件,购买附件前必须先购买对应的主件。我们需要在总预算内最大化物品的价值总和(价格乘以重要度)。
步骤分析:
-
数据存储: 将每个主件及其附件的信息分别存储。主件的价格和重要度存储在数组
v[i][0]
和p[i][0]
中,附件存储在对应的v[i][1]
、p[i][1]
和v[i][2]
、p[i][2]
中。 -
动态规划: 使用二维数组
f[i][j]
表示前i
个物品在预算j
时的最大价值。对于每个主件,考虑四种购买组合:仅主件、主件 + 附件1、主件 + 附件2、主件 + 两个附件。更新动态规划数组时,取这些组合中的最大值。 -
转移方程: 对于每个主件
i
,遍历所有可能的预算j
,检查四种组合是否可行,并更新最大价值。
代码
#include <bits/stdc++.h>
using namespace std;int n, m, v1, p1, q1;
int v[65][3], p[65][3]; // 主件i的附件价格和重要度
int f[65][32005]; // DP数组int main() {cin >> n >> m;for (int i = 1; i <= m; i++) {cin >> v1 >> p1 >> q1;if (q1) { // 附件// 找到对应的主件q1,将附件添加到其列表中if (v[q1][1]) { // 如果第一个附件位置已占,放第二个v[q1][2] = v1;p[q1][2] = p1;} else { // 否则放第一个v[q1][1] = v1;p[q1][1] = p1;}} else { // 主件v[i][0] = v1;p[i][0] = p1;}}// 动态规划处理for (int i = 1; i <= m; i++) { // 遍历所有物品,视为可能的主件for (int j = 0; j <= n; j++) {f[i][j] = f[i-1][j]; // 不选当前主件i// 检查四种组合是否可行,并更新最大值if (v[i][0] <= j) { // 仅主件f[i][j] = max(f[i][j], f[i-1][j - v[i][0]] + v[i][0] * p[i][0]);}if (v[i][0] + v[i][1] <= j) { // 主件+附件1f[i][j] = max(f[i][j], f[i-1][j - v[i][0] - v[i][1]] + v[i][0] * p[i][0] + v[i][1] * p[i][1]);}if (v[i][0] + v[i][2] <= j) { // 主件+附件2f[i][j] = max(f[i][j], f[i-1][j - v[i][0] - v[i][2]] + v[i][0] * p[i][0] + v[i][2] * p[i][2]);}if (v[i][0] + v[i][1] + v[i][2] <= j) { // 主件+两个附件f[i][j] = max(f[i][j], f[i-1][j - v[i][0] - v[i][1] - v[i][2]] + v[i][0] * p[i][0] + v[i][1] * p[i][1] + v[i][2] * p[i][2]);}}}cout << f[m][n]; // 输出最大价值return 0;
}
解释
-
输入处理: 读取物品信息,将主件存入对应位置,附件添加到所属主件的附件列表中。
-
动态规划初始化: 初始时没有购买任何物品,价值为
0
。 -
状态转移: 对于每个物品
i
(视为可能的主件),遍历预算j
,尝试四种购买组合,更新最大价值。 -
结果输出: 最终结果为处理完所有物品后的最大价值
f[m][n]
。
总结
该解法通过动态规划处理主件及其附件的组合购买情况,确保在预算内获得最大价值。每个主件的四种购买组合被逐一检查并更新最优解,最终结果正确且高效。