华为编程题:购物单问题
题目描述
王强今天很开心,公司发给N元的年终奖。王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 | 附件 |
电脑 | 打印机,扫描仪 |
书柜 | 图书 |
书桌 | 台灯,文具 |
工作椅 | 无 |
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。王强想买的东西很多,为了不超出预算,他把每件物品规定了一个重要度,分为 5 等:用整数 1 ~ 5 表示,第 5 等最重要。他还从因特网上查到了每件物品的价格(都是 10 元的整数倍)。他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第 j 件物品的价格为 v[j] ,重要度为 w[j] ,共选中了 k 件物品,编号依次为 j 1 , j 2 ,……, j k ,则所求的总和为:
v[j 1 ]*w[j 1 ]+v[j 2 ]*w[j 2 ]+ … +v[j k ]*w[j k ] 。(其中 * 为乘号)
请你帮助王强设计一个满足要求的购物单。
输入描述:
输入的第 1 行,为两个正整数,用一个空格隔开:N m
(其中 N ( <32000 )表示总钱数, m ( <60 )为希望购买物品的个数。)
从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q
(其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 ~ 5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)
输出描述:
输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值( <200000 )。
示例1
输出
2200思路:为背包问题的拓展问题:使用动态规划算法,先对物品进行分组(附件和对应主件放在一组,没有附件的单独一组),然后转化为分组的背包问题。
分组的背包问题:
6 分组的背包问题
6.1 问题
有 N 件物品和一个容量为 V 的背包。第 i 件物品的费用是 C i ,价值是 W i 。这些物品被划分为 K 组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
6.2 算法
这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设 F[k,v] 表示前 k 组物品花费费用 v 能取得的最大权值,则有:
F[k,v] = max{F[k − 1,v],F[k − 1,v − C i ] + W i | item i ∈ group k}
使用一维数组的伪代码如下:
for k ← 1 to K
for v ← V to 0
for all item i in group k
F[v] ← max{F[v],F[v − C i ] + W i }
6.1 问题
有 N 件物品和一个容量为 V 的背包。第 i 件物品的费用是 C i ,价值是 W i 。这些物品被划分为 K 组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
6.2 算法
这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设 F[k,v] 表示前 k 组物品花费费用 v 能取得的最大权值,则有:
F[k,v] = max{F[k − 1,v],F[k − 1,v − C i ] + W i | item i ∈ group k}
使用一维数组的伪代码如下:
for k ← 1 to K
for v ← V to 0
for all item i in group k
F[v] ← max{F[v],F[v − C i ] + W i }
本题如何如何转化为“分组的背包问题”:这个问题关键是如何将本题中的组(主件和附件组成的组)转换为“分组的背包问题”中的组,“分组的背包问题”中的组在选择的时候只能从中选择一件。转化方法是将本题中组元素组合成单独可选的,比如本题有组 A,元素为[x,y], 其中y是x的附件。选择y必须选择x. 在进行选择时,要么只选x, 要么选择x 和y. 所以可以转换为“分组的背包问题”中的组[x, [x, y]]. 这样就实现了组的转化,转化完即可按照“分组的背包问题”思路解决:
代码如下:
package com.huawei; import java.util.Scanner; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class Demo{ public static void main(String[] args){ Scanner sc = new Scanner(System.in); int m = sc.nextInt(); int n = sc.nextInt(); int[] v = new int[n+1]; int[] p = new int[n+1]; int[] q = new int[n+1]; int groups = 0; for(int i = 1; i<=n; i++){ v[i] = sc.nextInt(); p[i] = sc.nextInt(); q[i] = sc.nextInt(); if(q[i] == 0) { groups++; } } //分组 int[][] _v = new int[groups +1][4]; int[][] _p = new int[groups +1][4]; processData(q, v, p, _v, _p); int gc = _v.length; int[][] r = new int[gc][m+1]; for(int i = 1; i< gc; i++){ for(int j = 1; j<= m; j++){ int max = r[i-1][j]; for (int t = 1; t < _v[i].length; t++) { int tempv = _v[i][t]; int tempp = _p[i][t]; if(tempv != 0 && tempv <= j) { max = Math.max(max, r[i - 1][j - tempv] + tempp); } } r[i][j] = max; } } System.out.println(r[gc -1][m]); } private static void processData(int[] m, int[] v, int[] p, int[][] _v, int[][] _p) { Map<Integer, List<Integer>> groups = new HashMap<>(); for (int i = 1; i < m.length; i++) { if(m[i] == 0 ) { if(!groups.containsKey(i)) { List<Integer> temp = new ArrayList<Integer>(); temp.add(i); groups.put(i, temp); } }else { if (groups.containsKey(m[i])) { List<Integer> list = groups.get(m[i]); list.add(i); }else { List<Integer> temp = new ArrayList<Integer>(); temp.add(m[i]); temp.add(i); groups.put(m[i], temp); } } } int index = 1; for(List<Integer> list : groups.values()) { int size = list.size(); if(size == 1) { _v[index][1] = v[list.get(0)]; _p[index][1] = p[list.get(0)] * v[list.get(0)]; }else if (size == 2) { _v[index][1] = v[list.get(0)]; _p[index][1] = p[list.get(0)] * v[list.get(0)]; _v[index][2] = v[list.get(0)] + v[list.get(1)]; _p[index][2] = p[list.get(0)] * v[list.get(0)] + p[list.get(1)] * v[list.get(1)]; }else { _v[index][1] = v[list.get(0)]; _p[index][1] = p[list.get(0)]* v[list.get(0)]; _v[index][2] = v[list.get(0)] + v[list.get(1)]; _p[index][2] = p[list.get(0)] * v[list.get(0)] + p[list.get(1)] * v[list.get(1)]; _v[index][3] = v[list.get(0)] + v[list.get(1)] + v[list.get(2)]; _p[index][3] = p[list.get(0)] * v[list.get(0)] + p[list.get(1)]* v[list.get(1)] + p[list.get(2)]* v[list.get(2)]; } index++; }; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?