数据结构与算法-购物单
题目描述
王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 | 附件 |
---|---|
电脑 | 打印机,扫描仪 |
书柜 | 图书 |
书桌 | 台灯,文具 |
工作椅 | 无 |
如果要买归类为附件的物品,必须先买该附件所属的主件,且每件物品只能购买一次。
每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。
王强查到了每件物品的价格(都是 10 元的整数倍),而他只有 N 元的预算。除此之外,他给每件物品规定了一个重要度,用整数 1 ~ 5 表示。他希望在花费不超过 N 元的前提下,使自己的满意度达到最大。
满意度是指所购买的每件物品的价格与重要度的乘积的总和,假设设第
输入描述:
输入的第 1 行,为两个正整数 N,m,用一个空格隔开:
(其中 N ( N<32000 )表示总钱数, m (m <60 )为可购买的物品的个数。)
从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q
(其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 ~ 5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)
输出描述:
输出一个正整数,为张强可以获得的最大的满意度。
示例
输入:
50 5
20 3 5
20 3 5
10 3 0
10 2 0
10 1 0
输出:
130
说明:
由第1行可知总钱数N为50以及希望购买的物品个数m为5;
第2和第3行的q为5,说明它们都是编号为5的物品的附件;
第4~6行的q都为0,说明它们都是主件,它们的编号依次为3~5;
所以物品的价格与重要度乘积的总和的最大值为10*1+20*3+20*3=130
问题分析
购物车本质上还是 0-1背包问题,只不过多了主件和附件。假设先不看附件,那么就和 0-1背包一样了。附件不能单独出现,要依赖于主件。
对应于背包问题,主件的个数就是物品的个数,考虑每个主件时要考虑可能出现的情况。
考虑每个物品时要考虑每种可能出现的情况,
- 主件,
- 主件+附件1,
- 主件+附件2,
- 主件+附件1+附件2,
在以上四种情况中找到最大值就能回归到0-1背包问题。所以我们先考虑一下普通的0-1背包问题。
对于一个可承重
那么对于“购物单”这道题,我们可以有如下抽象:
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
int N, m; // N 奖金 m 物品个数
cin >> N >> m;
N /= 10; // 由于所有的价格都是10的整倍数,所以可以均除10以简化运算复杂度
int price, priority, hasAttachments;
// 使用一个(m+1)X6的数组存储数据,m+1是根据物品编号,0作废;6考虑可能有附件的最多的情况
vector<vector<int>> data(m+1, vector<int>(6, 0));
for(int i = 1; i <= m; i++){
cin >> price >> priority >> hasAttachments;
// 主件
if(hasAttachments == 0){
data[i][0] = price/10;
data[i][1] = priority;
}
// 第一个附件
else if(data[hasAttachments][2] == 0){
data[hasAttachments][2] = price/10;
data[hasAttachments][3] = priority;
}
// 第二个附件
else {
data[hasAttachments][4] = price/10;
data[hasAttachments][5] = priority;
}
}
vector<vector<int>> dp(m+1, vector<int>(N+1, 0));
for(int i = 1; i < m+1; i++){
for(int j = 1; j < N+1; j++){
int pricePrime = data[i][0];
int priceAtta1 = data[i][2];
int priceAtta2 = data[i][4];
int priorPrime = data[i][1];
int priorAtta1 = data[i][3];
int priorAtta2 = data[i][5];
dp[i][j] = j >= pricePrime ? max(dp[i-1][j - pricePrime]
+ priorPrime * pricePrime,
dp[i-1][j]) : dp[i-1][j];
dp[i][j] = j >= (pricePrime + priceAtta1) ? max(dp[i-1][j - pricePrime - priceAtta1]
+ priorPrime * pricePrime
+ priorAtta1 * priceAtta1,
dp[i][j]) : dp[i][j];
dp[i][j] = j >= (pricePrime + priceAtta2) ? max(dp[i-1][j - pricePrime - priceAtta2]
+ priorPrime * pricePrime
+ priorAtta2 * priceAtta2,
dp[i][j]) : dp[i][j];
dp[i][j] = j >= (pricePrime + priceAtta1 + priceAtta2) ?
max(dp[i-1][j - pricePrime - priceAtta1 - priceAtta2]
+ priorPrime * pricePrime
+ priorAtta1 * priceAtta1
+ priorAtta2 * priceAtta2,
dp[i][j]) : dp[i][j];
}
}
cout << dp[m][N] * 10 <<endl;
return 0;
}
转化为01背包问题
在解决完全背包问题时,我们考虑了每个物品可能出现的次数。在解决购物车问题时,每个物品时要考虑每种可能出现的情况,
- 主件,
- 主件+附件1,
- 主件+附件2,
- 主件+附件1+附件2,
我们将存在的情况当作新的物品,这样购物车问题就可以当作 01 背包问题。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)