题解:学而思编程 智能饭盒
【题目来源】
学而思编程:智能饭盒
【题目描述】
有 \(n\) 个食物和一个饭盒。第 \(i\) 个食物含有 \(a_i\) 克脂肪和 \(b_i\) 克蛋白质。
小猴要选择一些食物装进饭盒里,这是个智能饭盒,可以测定食物的脂肪和蛋白质含量,小猴想要选出的食物合计至少含有 \(x\) 克脂肪和 \(y\) 克蛋白质。他最少需要选择多少个食物装进饭盒?
【输入】
第 \(1\) 行,\(1\) 个正整数 \(n\)
第 \(2\) 行,\(2\) 个正整数 \(x,y\)
接下来n行,每行两个正整数\(a_i,b_i\)
【输出】
\(1\) 个整数,需要选择的最少食物数;如果无法得到要求的脂肪和蛋白质含量,输出 \(−1\)。
【输入样例】
3
5 6
2 1
3 4
2 3
【输出样例】
2
【核心思想】
-
问题分析:给定 \(n\) 个食物,每个食物提供 \(a_i\) 克脂肪和 \(b_i\) 克蛋白质,要求选出最少数量的食物使得总脂肪至少 \(x\) 克且总蛋白质至少 \(y\) 克。这是一个二维 0-1 背包问题,关键在于将"双维度资源约束下的最少物品数"转化为 DP 状态设计。
-
算法选择:
- 二维 0-1 背包:将脂肪 \(j\) 和蛋白质 \(k\) 作为两个资源维度,物品数量作为待最小化的目标值
- 反向遍历优化:两个维度均反向遍历,确保每个食物只被选取一次
- 状态压缩:利用
max(j - a_i, 0)和max(k - b_i, 0)将超过目标值的状态压缩到边界,降低状态空间
-
关键步骤:
- 初始化:
- 读取 \(n\)(食物数量)、\(x\)(目标脂肪)、\(y\)(目标蛋白质)
- 读取每个食物的 \(a_i\) 和 \(b_i\)
f[j][k]表示达到至少 \(j\) 克脂肪和至少 \(k\) 克蛋白质所需的最少食物数memset(f, 0x3f, sizeof f):初始化为正无穷(不可达)f[0][0] = 0:不选任何食物时,脂肪和蛋白质均为 \(0\),需要 \(0\) 个食物
- DP 转移(遍历 \(i\) 从 \(1\) 到 \(n\)):
- 遍历 \(j\) 从 \(x\) 到 \(0\)(脂肪维度反向):
- 遍历 \(k\) 从 \(y\) 到 \(0\)(蛋白质维度反向):
f[j][k] = \min(f[j][k], f[\max(j - a_i, 0)][\max(k - b_i, 0)] + 1)- 不选第 \(i\) 个食物:保持
f[j][k]不变 - 选第 \(i\) 个食物:从"扣除该食物贡献后的状态"转移,食物数加 \(1\)
\max(j - a_i, 0)和\max(k - b_i, 0):将超过目标的状态压缩到 \(0\),避免数组越界并统一处理"超额满足"的情况
- 遍历 \(k\) 从 \(y\) 到 \(0\)(蛋白质维度反向):
- 遍历 \(j\) 从 \(x\) 到 \(0\)(脂肪维度反向):
- 判断输出:
- 若
f[x][y] \geq 0x3f3f3f3f:状态不可达,输出 \(-1\) - 否则输出
f[x][y]
- 若
- 初始化:
-
时间/空间复杂度:
- 时间复杂度:\(O(n \times x \times y)\),\(n\) 个物品,每个物品更新 \(x \times y\) 个状态
- 空间复杂度:\(O(x \times y)\),二维 DP 数组 \(f[0..x][0..y]\)
-
二维 0-1 背包的核心思想:
- 双维度状态设计:
f[j][k]同时追踪两个资源维度(脂肪和蛋白质),将"满足双约束的最少物品数"作为优化目标 - 反向遍历保证 0-1 性:两个维度均从大到小遍历,确保第 \(i\) 个食物的状态转移只使用第 \(i-1\) 个食物的状态,每个食物最多选一次
- 状态压缩技巧:用
\max(j - a_i, 0)将超过目标值的状态映射到 \(0\),既避免数组越界,又将"超额满足"统一为"恰好满足",简化状态空间 - 最少物品数的转化:传统背包求最大价值,本题将"物品数量"作为 DP 值,目标资源作为状态维度,实现"逆向"背包
- 适用于"双资源约束下最少选取物品数"的组合优化问题,状态压缩可有效降低空间复杂度
- 双维度状态设计:
【解题思路】

【算法标签】
01背包
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, x, y;
int f[305][305], a[305], b[305];
int main()
{
cin >> n >> x >> y; // 输入物品数量和目标值
for (int i = 1; i <= n; i++) {
cin >> a[i] >> b[i]; // 输入每个物品的属性
}
// 初始化DP数组为无穷大
memset(f, 0x3f, sizeof f);
f[0][0] = 0; // 不需要任何物品就可以达到(0,0)
// 动态规划
for (int i = 1; i <= n; i++) { // 考虑前i个物品
for (int j = x; j >= 0; j--) { // 反向遍历,实现0-1背包
for (int k = y; k >= 0; k--) {
// 状态转移:选或不选第i个物品
f[j][k] = min(f[j][k], f[max(j - a[i], 0)][max(k - b[i], 0)] + 1);
}
}
}
// 输出结果
if (f[x][y] >= 0x3f3f3f3f) // 如果不可达
cout << -1;
else
cout << f[x][y];
return 0;
}
【运行结果】
3
5 6
2 1
3 4
2 3
2
浙公网安备 33010602011771号