组装电脑
题目描述
小U是一位电脑外壳供应商,而小S是一位电脑零件供应商。他们希望合作组装电脑。小U生产了n个电脑外壳,每个外壳的售价是a,而小S生产了n个零件,每个零件的售价是b。当一个外壳与一个零件配对时,电脑的售价是外壳售价与零件售价的和。
他们需要找出一些外壳和零件的配对,使得这些配对形成的电脑售价全部相同,问最多可以组装多少部售价相同的电脑。
测试样例
-
样例1:
- 输入:
n = 4, a = [1, 2, 3, 4], b = [1, 2, 4, 5]
- 输出:
3
- 输入:
-
样例2:
- 输入:
n = 3, a = [1, 1, 2], b = [2, 2, 3]
- 输出:
2
- 输入:
-
样例3:
- 输入:
n = 5, a = [5, 5, 5, 5, 5], b = [1, 2, 3, 4, 5]
- 输出:
1
- 输入:
1. 初步分析问题
问题理解
需要找到方法使得电脑外壳和零件的配对售价完全相同,并且尽可能多地配对。
数据结构选择
- 哈希表(字典):记录每个可能售价的出现次数,帮助统计售价配对数量。
算法步骤
- 计算所有可能的售价:遍历外壳售价
a_i
和零件售价b_j
,计算其和a_i + b_j
。 - 统计每个售价的出现次数:使用哈希表记录每个售价的配对数量。
- 找出出现次数最多的售价:遍历哈希表,确定最多可以组装的电脑数量。
关键点
- 使用哈希表高效统计和查找售价。
- 遍历所有组合计算售价。
2. 尝试实现代码
def solution(n: int, a: list, b: list) -> int:
# 初始化哈希表
price_count = {}
# 遍历所有可能的售价
for i in range(n):
for j in range(n):
price = a[i] + b[j]
# 更新哈希表
if price in price_count:
price_count[price] += 1
else:
price_count[price] = 1
# 找出出现次数最多的售价
max_count = 0
for count in price_count.values():
if count > max_count:
max_count = count
return max_count
# 测试样例
print(solution(n = 4, a = [1, 2, 3, 4], b = [1, 2, 4, 5])) # 应输出: 3
print(solution(n = 3, a = [1, 1, 2], b = [2, 2, 3])) # 应输出: 2
print(solution(n = 5, a = [5, 5, 5, 5, 5], b = [1, 2, 3, 4, 5])) # 应输出: 1
3. 第一次发现问题
后面两组输出错误:
print(solution(n = 3, a = [1, 1, 2], b = [2, 2, 3]))
应该输出2
,实际输出4
print(solution(n = 5, a = [5, 5, 5, 5, 5], b = [1, 2, 3, 4, 5]))
应该输出1
,实际输出5
错误分析
由于重复使用某个零件与多个外壳配对,需确保每个外壳和零件只能组合一次。
4. 尝试解决问题
使用布尔数组来标记每个外壳和零件是否已被使用。这种方法可以有效避免重复使用相同的外壳和零件,同时处理售价重复的情况。
- 售价组合生成:首先生成所有可能的售价组合,并存储在
price_count
字典中。 - 布尔数组:使用
used_shells
和used_parts
布尔数组,分别跟踪外壳和零件的使用情况。 - 搭配计算:遍历每个售价的组合,当找到一个未使用的外壳和零件时,将其标记为已使用,并增加可组装电脑的数量。
- 结果输出:确保计算出的最大可组装电脑数量符合预期。
def max_computers(n, a, b):
# 创建一个字典来存储售价及其对应的组合
price_count = {}
# 生成每对外壳和零件组合的售价
for i in range(n):
for j in range(n):
price = a[i] + b[j]
if price not in price_count:
price_count[price] = []
price_count[price].append((i, j))
# 计算可以组装的电脑的最大数量
max_computer_count = 0
# 使用布尔数组来跟踪已使用的外壳和零件
used_shells = [False] * n
used_parts = [False] * n
# 遍历每个售价组合,计算可以组装的电脑的数量
for price, combinations in price_count.items():
for shell_index, part_index in combinations:
# 检查该外壳和零件是否已经使用
if not used_shells[shell_index] and not used_parts[part_index]:
max_computer_count += 1
used_shells[shell_index] = True # 标记外壳为已使用
used_parts[part_index] = True # 标记零件为已使用
break # 找到一对后跳出内层循环
return max_computer_count
# 测试样例
print(max_computers(4, [1, 2, 3, 4], [1, 2, 4, 5])) # 应输出: 3,输出4
print(max_computers(3, [1, 1, 2], [2, 2, 3])) # 应输出: 2,输出2
print(max_computers(5, [5, 5, 5, 5, 5], [1, 2, 3, 4, 5])) # 应输出: 1,输出5
5. 第二次发现问题
在尝试解决问题时,发现输出不正确的情况:
print(max_computers(4, [1, 2, 3, 4], [1, 2, 4, 5]))
应该输出3
,但实际输出4
。这是因为在计算售价组合时,虽然确保了外壳和零件的唯一性,但未考虑每个售价的组合可能会产生多个计算。print(max_computers(5, [5, 5, 5, 5, 5], [1, 2, 3, 4, 5]))
应该输出1
,但实际输出5
。这表明当售价组合的计算未充分管理重复使用的情况时,可能导致结果的过高预估。
6. 最终解决方案
逻辑思路
- 创建售价组合字典:使用字典将每个售价和其对应的组合关系存储起来。
- 生成售价组合:通过双重循环遍历所有外壳和零件的组合,记录每种售价下的组合情况。
- 排序售价组合:按组合数量对售价进行排序,优先处理可组装数量多的售价。
- 计算最大组合数量:遍历排列后的售价组合,检查并计算实际可组装的电脑数量。
- 返回最大数量:返回所有售价组合中可以组装的最大电脑数量。
步骤代码详细解释
创建售价组合字典:
price_count = {}
- 初始化一个空字典
price_count
。这个字典将用于存储不同售价(外壳和零件组合的和)及其对应的组合信息。 - 通过字典的方式管理售价和组合的关系,使得后续的查找和处理更为高效。
生成售价组合:
for i in range(n):
for j in range(n):
price = a[i] + b[j]
if price not in price_count:
price_count[price] = []
price_count[price].append((i, j))
- 使用双重循环遍历外壳
a
和零件b
的所有组合。 - 计算售价
price
,即外壳和零件的和。 - 如果该售价在字典中尚不存在,则初始化一个空列表。
- 将当前组合的索引
(i, j)
添加到对应售价的列表中。 - 这一步创建了一个售价和其对应组合的映射,方便后续根据售价进行组合数量的计算。
- 将组合的索引存储在列表中,能够在后续步骤中快速检索和使用。
排序售价组合:
sorted_prices = sorted(price_count.items(), key=lambda x: len(x[1]), reverse=True)
- 对字典
price_count
中的项进行排序,按照每个售价对应组合的数量进行降序排列。 - 这样做的目的是优先处理那些组合数量较多的售价,从而提高了可能组装电脑的效率。
- 通过使用
len(x[1])
计算每个售价对应的组合数量,确保我们处理的是最有潜力的售价。
计算最大组合数量:
new_max_computer_count = []
- 初始化一个空列表
new_max_computer_count
,用于存储每个售价下可以组装的最大电脑数量。 - 在后续处理中,我们将记录每个售价组合的最大可组装数量,以便最后返回整体的最大值。
遍历售价组合并计算可组装数量:
for price, combinations in sorted_prices:
max_computer_count = len(combinations)
current_count = 0
used_shells = [False] * n
used_parts = [False] * n
- 对每个售价及其组合进行遍历。
- 初始化
max_computer_count
为当前售价组合的数量。 - 初始化
current_count
为0
,用于跟踪当前售价下实际可以组装的数量。 - 创建两个布尔数组
used_shells
和used_parts
,用于标记哪些外壳和零件已经被使用。 - 这一步为计算提供了基础,确保我们能追踪每个售价的组合使用情况,避免重复使用外壳和零件。
检查并计数可用组合:
for shell_index, part_index in combinations:
if not used_shells[shell_index] and not used_parts[part_index]:
current_count += 1
used_shells[shell_index] = True
used_parts[part_index] = True
- 遍历当前售价的所有组合,检查每个组合中的外壳和零件是否已被使用。
- 如果外壳和零件都未被使用,则增加
current_count
,并将对应的外壳和零件标记为已使用。 - 通过这一步,实际上计算出在当前售价条件下,可以组装的电脑数量,确保外壳和零件没有重复使用。
更新最大组合数量:
max_computer_count = min(max_computer_count, current_count)
new_max_computer_count.append(max_computer_count)
- 使用
min()
函数将current_count
与max_computer_count
进行比较,确保只保留最小值。 - 将这个值存入
new_max_computer_count
列表中。 - 保证每个售价返回的组合数量是基于实际可组装数量而更新的,避免过高的预估。
返回最大数量:
return max(new_max_computer_count)
- 返回列表
new_max_computer_count
中的最大值。 - 最终的返回值是所有售价组合中可以组装的最大电脑数量,符合题目要求。
完整代码
def solution(n, a, b):
price_count = {}
for i in range(n):
for j in range(n):
price = a[i] + b[j]
if price not in price_count:
price_count[price] = []
price_count[price].append((i, j))
sorted_prices = sorted(price_count.items(), key=lambda x: len(x[1]), reverse=True)
new_max_computer_count = []
for price, combinations in sorted_prices:
max_computer_count = len(combinations)
current_count = 0
used_shells = [False] * n
used_parts = [False] * n
for shell_index, part_index in combinations:
if not used_shells[shell_index] and not used_parts[part_index]:
current_count += 1
used_shells[shell_index] = True
used_parts[part_index] = True
max_computer_count = min(max_computer_count, current_count)
new_max_computer_count.append(max_computer_count)
return max(new_max_computer_count)
print(solution(4, [1, 2, 3, 4], [1, 2, 4, 5]))
print(solution(3, [1, 1, 2], [2, 2, 3]))
print(solution(5, [5, 5, 5, 5, 5], [1, 2, 3, 4, 5]))
7. 总结与收获
-
深入问题分析:在解决问题的过程中,仔细理解题目要求是关键。通过明确问题的核心,能够更高效地找到合适的解决方案,避免在不必要的细节上浪费时间。
-
数据结构的巧妙运用:哈希表和布尔数组在解题中发挥了重要作用。哈希表帮助管理售价组合,提升了代码的清晰度,而布尔数组有效地追踪了已使用的外壳和零件,使数据管理变得更加简便。
-
逐步调试的重要性:多次测试和细致的检验彰显了调试过程的必要性。及时发现并修复错误,确保了最终结果的准确性。每一次小问题的解决都带来了成就感,让整个过程更具乐趣。
-
算法优化的乐趣:对售价组合的排序优化了计算效率,优先处理组合数量多的售价,使得整体流程更加流畅。这个过程不仅提高了程序的运行速度,也增强了对算法设计的理解。
-
实践中的成长:编码过程中不断尝试和调整,加深了对数据结构和算法应用的认识。每个挑战都是一个学习的机会,积累的经验为未来处理类似问题打下了坚实基础。
-
注重代码注释与文档化:为代码添加详细注释,使得在后续回顾和维护时能够迅速理解逻辑。