洛谷P1064 金明的预算方案 dp 01背包

本文深入探讨了带有限制的01背包问题,采用动态规划方法解决,并通过具体代码实例展示了如何处理主件与附件的组合购买问题,以达到最大价值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题解

带有限制的01背包 最开始以为是状压dp发现状态太多
按照01背包的写法 将主件和附件看作同一个物品 处理时计算只买主件、买主件和第一个附件、买主件和第二个附件、买主件和两个附件四种情况进行转移 转移枚举容量时这四种情况并行操作就不会相互造成贡献 不同物品之间则可以
接受数据时直接将权值*价格方便计算

AC代码

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int MAXN = 100;
const int MAXM = 4e4;

struct node
{
	int v, w; //重量 价值
};
vector<node> a[MAXN];

int d[MAXM];
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int N, M; //物品 容量
	cin >> M >> N;
	for (int i = 1; i <= N; i++)
	{
		int v, p, q;
		scanf("%d%d%d", &v, &p, &q);
		p *= v; //直接计算价值
		if (q)
			a[q].push_back({ v, p });
		else
			a[i].push_back({ v, p });
	}
	for (int i = 1; i <= N; i++)
		if (a[i].size())
			for (int j = M; j >= 0; j--)
			{
				node f = a[i][0];
				if (j >= f.v) //只买主件
					d[j] = max(d[j], d[j - f.v] + f.w);
				if (a[i].size() > 1)
				{
					node s1 = a[i][1];
					if (j >= f.v + s1.v) //主+1附
						d[j] = max(d[j], d[j - f.v - s1.v] + f.w + s1.w); 
					if (a[i].size() > 2)
					{
						node s2 = a[i][2];
						if (j >= f.v + s2.v) //主+2附
							d[j] = max(d[j], d[j - f.v - s2.v] + f.w + s2.w);
						if (j >= f.v + s1.v + s2.v) //主+12附
							d[j] = max(d[j], d[j - f.v - s1.v - s2.v] + f.w + s1.w + s2.w);
					}
				}
			}
	cout << d[M] << endl;

	return 0;
}
<think>嗯,用户问的是洛谷P1064的题解和示例代码。首先,我需要回忆一下这道题的具体内容。洛谷的题目编号P1064通常对应的是“金明预算方案”,这是一道动态规划的经典题目,属于背包问题的变种。用户可能已经尝试过但遇到了困难,需要详细的思路和代码示例。 首先,我需要确认题目要求。根据记忆,这道题的大意是,金明有N元钱,想要购买一些物品,这些物品分为主件和附件,每个主件可以有0、1或2个附件。购买附件的前提是已经购买了对应的主件。目标是在不超过总金额的情况下,使得每件物品的价格与重要度的乘积的总和最大。这明显是一个依赖背包问题,需要处理主件和附件的组合情况。 接下来,我需要考虑如何将这个问题转化为动态规划模型。通常,背包问题中的依赖关系可以通过分组背包来处理,每个主件及其附件的组合形成一个组,组内有多种选择方式。例如,一个主件可能有四种选择:不买、只买主件、买主件加第一个附件、买主件加第二个附件,或者买主件加两个附件,具体取决于附件的数量。 然后,用户可能需要具体的状态转移方程。假设总金额为V,每个主件i的价格为v[i],重要度为w[i],附件1为v1[i],w1[i],附件2为v2[i],w2[i]。那么对于每个主件,有几种可能的组合,总共有四种情况:主件、主件+附件1、主件+附件2、主件+附件1+附件2。这样,在动态规划过程中,对于每个主件,需要遍历所有可能的组合,并更新dp数组。 需要注意的是,附件不能单独存在,必须依附于主件。因此,在预处理时,需要将主件和附件的组合预先计算好,然后在动态规划时处理每个组内的所有可能性。 接下来,考虑代码结构。通常,这类问题需要先读取输入,将主件和附件分开存储,然后构建每个主件的所有可能组合,之后使用分组背包的模板进行处理。例如,可以使用一个数组dp[j]表示花费j元所能获得的最大价值,然后对每个主件组中的每个可能的组合,从后往前更新dp数组。 用户可能遇到的难点在于如何处理主件和附件的关系,以及如何枚举所有可能的组合。比如,如何判断某个主件是否有附件,如何计算不同组合下的价格和重要度乘积之和。此外,还需要注意时间复杂度,避免不必要的循环。 在示例代码中,应该展示如何存储主件和附件的信息,如何生成每个主件的可能选项,然后应用动态规划。例如,可以用结构体或类来存储每个物品的信息,然后遍历每个主件,生成其所有可能的组合,再更新dp数组。 另外,用户可能需要了解为什么这样处理是正确的,也就是动态规划的状态转移是否覆盖了所有可能的情况,是否遗漏了某些组合。因此,在解释思路时,需要强调分组背包的处理方式,确保每个主件组内的所有情况都被考虑进去。 最后,需要验证代码的正确性,比如测试样例是否正确,是否有处理边界情况,比如没有附件的情况,或者附件数量不足两个的情况。同时,代码需要符合题目的输入输出格式,避免因为格式错误导致提交失败。 总结一下,回答应该包括问题分析(依赖背包转化为分组背包)、动态规划思路(状态定义、转移方程)、代码实现步骤(输入处理、组合生成、DP更新),以及示例代码和可能的优化点。同时,根据用户提供的引用,可能需要参考类似题目的解法,比如引用5中的A-B数对的暴力解法,但这里显然需要更高效的动态规划方法。</think>### 洛谷P1064 金明预算方案 题解 #### 问题分析 这是一个依赖型背包问题,主件与附件构成依赖关系。需在有限预算下选择主件及其附件组合,使得总价值(价格×重要度)最大化[^5]。 #### 解题思路 1. **输入处理** 将物品分类为主件和附件,记录每个主件的附属关系。 2. **组合生成** 对每个主件生成4种可能组合: - 仅主件 - 主件+附件1 - 主件+附件2 - 主件+附件1+附件2 3. **动态规划** 使用分组背包思想,状态转移方程: $$ dp[j] = \max(dp[j],\ dp[j - cost] + value) $$ 其中$cost$为组合价格,$value$为组合价值。 #### 示例代码(C++) ```cpp #include <iostream> #include <vector> using namespace std; struct Item { int v, w; vector<Item> sub; }; int main() { int N, m; cin >> N >> m; vector<Item> items(m+1); // 读取输入并建立主附件关系 for (int i = 1; i <= m; ++i) { int v, p, q; cin >> v >> p >> q; if (q == 0) { items[i].v = v; items[i].w = v * p; } else { items[q].sub.push_back({v, v*p}); } } vector<int> dp(N+1, 0); // 动态规划处理 for (int i = 1; i <= m; ++i) { if (items[i].v == 0) continue; // 跳过附件 vector<pair<int, int>> groups; int main_v = items[i].v, main_w = items[i].w; groups.emplace_back(main_v, main_w); // 生成所有组合 if (!items[i].sub.empty()) { Item s1 = items[i].sub[0]; groups.emplace_back(main_v+s1.v, main_w+s1.w); if (items[i].sub.size() > 1) { Item s2 = items[i].sub[1]; groups.emplace_back(main_v+s2.v, main_w+s2.w); groups.emplace_back(main_v+s1.v+s2.v, main_w+s1.w+s2.w); } } // 分组背包DP for (int j = N; j >= 0; --j) { for (auto [cost, value] : groups) { if (j >= cost) { dp[j] = max(dp[j], dp[j - cost] + value); } } } } cout << dp[N]; return 0; } ``` #### 代码说明 1. 使用结构体存储物品信息,`sub`字段存储附件 2. 预处理时生成所有有效组合 3. 倒序遍历金额防止重复计算 4. 时间复杂度:$O(N \times M)$,其中M为有效组合数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值