奇怪的午餐盒
题目回顾
我们需要从N种午餐盒中选择若干个,使得:
- 总章鱼烧数量 ≥ X
- 总鲷鱼烧数量 ≥ Y
- 选择的午餐盒数量最少
每种午餐盒只能选一次(0-1背包问题)。
解题思路
这是一个典型的二维费用的0-1背包问题,我们可以使用动态规划(DP)来解决。
DP状态定义
定义 dp[i][j]
表示总章鱼烧 ≥ i,总鲷鱼烧 ≥ j 的最小午餐盒数量。
状态转移
对于每个午餐盒(A_i, B_i),我们有两种选择:
- 不选:
dp[i][j]
保持不变。 - 选:更新
dp[min(i + A_i, X)][min(j + B_i, Y)]
为min(dp[new_i][new_j], dp[i][j] + 1)
。
初始化
初始时,dp[0][0] = 0
(不选任何午餐盒时,数量为0),其他位置设为无穷大(表示不可达)。
最终答案
我们需要在所有 i ≥ X
和 j ≥ Y
的情况下,找到最小的 dp[i][j]
。
代码注释
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl "\n"
const int INF = 0x7fffffff; // 定义无穷大
const int N = 3e2+5; // 定义最大可能的X和Y范围
struct node{
int first,secend; // first表示章鱼烧数量,secend表示鲷鱼烧数量
}a[N]; // 存储每种午餐盒的信息
int n, x, y; // n表示午餐盒种类数,x和y表示目标数量
int main() {
cin>>n>>x>>y; // 输入n, x, y
for(int i=1;i<=n;i++){
cin>>a[i].first>>a[i].secend; // 输入每种午餐盒的章鱼烧和鲷鱼烧数量
}
// 初始化DP表,dp[i][j]表示达到i章鱼烧和j鲷鱼烧所需的最小午餐盒数
vector<vector<int>> dp(N,vector<int>(N,INF));
dp[0][0] = 0; // 初始状态:不选任何午餐盒时,数量为0
// 遍历每种午餐盒
for(int i=1;i<=n;i++){
int p = a[i].first, q = a[i].secend; // 当前午餐盒的章鱼烧和鲷鱼烧数量
// 逆向更新DP表,避免重复选择
for(int i=x;i>=0;i--){
for(int j=y;j>=0;j--){
if(dp[i][j]==INF) continue; // 如果当前状态不可达,跳过
// 计算选择当前午餐盒后的新状态
int one = min(i+p, x); // 章鱼烧数量不超过x
int two = min(j+q, y); // 鲷鱼烧数量不超过y
// 更新新状态的最小午餐盒数
dp[one][two] = min(dp[one][two], dp[i][j]+1);
}
}
}
// 输出结果
if(dp[x][y]==INF) cout<<-1; // 如果无法达到目标,输出-1
else cout<<dp[x][y]; // 否则输出最小午餐盒数
return 0;
}
代码解释
- 输入处理:读取N, X, Y以及每个午餐盒的A_i和B_i。
- DP初始化:
dp
表初始化为INF,表示不可达,dp[0][0] = 0
表示不选任何午餐盒时数量为0。 - DP更新:
- 遍历每个午餐盒。
- 逆向更新DP表(类似0-1背包问题,避免重复选择)。
- 更新时,
one
和two
被限制在X和Y以内,避免计算不必要的大数值。
- 结果输出:如果
dp[x][y]
仍为INF,说明无法满足条件,输出-1;否则输出最小数量。
复杂度分析
- 时间复杂度:O(NXY) = 300×300×300 = 27,000,000,在C++中是可接受的。
- 空间复杂度:O(XY) = 300×300 = 90,000,使用滚动数组优化后空间足够。