描述
题目描述
若两个正整数的和为素数,则这两个正整数称之为“素数伴侣”,如2和5、6和13,它们能应用于通信加密。现在密码学会请你设计一个程序,从已有的 N ( N 为偶数)个正整数中挑选出若干对组成“素数伴侣”,挑选方案多种多样,例如有4个正整数:2,5,6,13,如果将5和6分为一组中只能得到一组“素数伴侣”,而将2和5、6和13编组将得到两组“素数伴侣”,能组成“素数伴侣”最多的方案称为“最佳方案”,当然密码学会希望你寻找出“最佳方案”。
输入:有一个正偶数 n ,表示待挑选的自然数的个数。后面给出 n 个具体的数字。
输出:输出一个整数 K ,表示你求得的“最佳方案”组成“素数伴侣”的对数。
数据范围: 1≤𝑛≤100 ,输入的数据大小满足 2≤𝑣𝑎𝑙≤30000
输入描述:
输入说明
1 输入一个正偶数 n
2 输入 n 个整数
输出描述:
求得的“最佳方案”组成“素数伴侣”的对数。
示例1
输入:4
2 5 6 13
输出:2
示例2
输入:2
3 6
输出:0
看不懂系列
import math # 导入数学模块,以便使用数学函数
import sys # 导入系统模块,可以进行系统级操作
# 定义一个函数,用于判断一个数是否为质数
def is_prime(x: int) -> bool:
if x < 2: # 如果 x 小于 2,则不是质数
return False
# 从 2 到 x 的平方根之间的所有数中检查是否有能整除 x 的数
for i in range(2, int(math.sqrt(x)) + 1):
if x % i == 0: # 如果找到一个数能整除 x,则 x 不是质数
return False
return True # 如果没有找到能整除 x 的数,则 x 是质数
# 定义一个函数 f1,进行匹配操作
def f1(m: int, lr: list[list[int]], vis: list[int]) -> bool:
for j in range(len(lr)): # 遍历 lr 列表
if vis[j] == 1: # 如果 vis[j] 为 1,表示已经访问过,跳过
continue
# 如果 m 和 lr[j][0] 的和是质数
if is_prime(m + lr[j][0]):
vis[j] = 1 # 标记为已访问
# 如果 lr[j][1] 为空,或者递归调用 f1 成功
if lr[j][1] == 0 or f1(lr[j][1], lr, vis):
lr[j][1] = m # 更新 lr[j][1] 为 m
return True
return False # 如果没有找到匹配,返回 False
# 输入 n 的值
n = int(input())
# 输入列表,并将其映射为整数
li = map(int, input().split())
# 初始化两个空列表,分别存放奇数和偶数
odd_li, even_li = [], []
# 遍历输入的整数列表
for item in li:
if item % 2 == 0: # 如果是偶数,添加到 even_li 列表
even_li.append([item, 0])
else: # 如果是奇数,添加到 odd_li 列表
odd_li.append(item)
# 初始化计数器
count = 0
# 遍历 odd_li 列表中的每个奇数
for item in odd_li:
vis = [0] * len(even_li) # 初始化 vis 列表,用于标记访问状态
if f1(item, even_li, vis): # 如果 f1 函数返回 True
count += 1 # 增加计数器
# 输出计数结果
print(count)
整体逻辑
- 将输入的整数分为奇数和偶数。
- 利用
f1
函数尝试为每个奇数找到一个偶数匹配,使得它们之和为质数。 - 使用
vis
数组记录匹配过程中访问过的偶数,避免重复检查。 - 通过递归和深度优先搜索实现匹配。
- 输出所有成功匹配的数量。
这段代码的核心在于利用质数的性质和深度优先搜索来实现一个二分图的匹配问题。通过将奇数和偶数看作图的两部分,尝试找到满足条件的边,即奇数与偶数之和为质数的配对。
素数(质数)伴侣(转自boxiong.h题解)
匈牙利算法(非完全版),二分图最大匹配
link: https://zh.wikipedia.org/wiki/匈牙利算法
匈牙利算法举例:4个工人(row)分配四项任务(col),寻找最油分配方案
1. 将工人和任务分开为,生成每一行代表一个工人,每一列代表一项任务的矩阵
矩阵则为每个工人对不同任务的施工成本
2. 完美情况:每个工人对应一项擅长的任务,则直接分配
3. 遍历情况:当某两个工人对某项任务最熟悉且熟悉程度相同,那么需要遍历这两个工人对另外三项任务的熟悉程度
4. 如果在剩下三项任务中,这两个工人有一个更熟悉的,那么将该任务分配给熟悉的工人。
This case:
1. 我们将数字氛围奇偶数后,奇数对比工人,偶数对比任务项
2. 施工成本对应奇偶数只和
3. 生成质素判定方程,判定矩阵中数值是否为质数。
从而生成一个质数为true(1),非质数为0的矩阵,便与后续判定
4. 后续思路在find(x)函数中。
-> connection_b可以理解为任务列表,用于判断该任务是否已被领取。
在此案例则为,偶数储存列别,储存该偶数是否已于某个奇数组合成质数
-> used_b为临时储存信息44
import math # 导入数学模块
# 判断一个数是否为质数
def is_prime(number: int) -> bool:
if number < 2: # 小于2的数不是质数
return False
# 检查从2到sqrt(number)之间是否存在因子
for i in range(2, int(math.sqrt(number)) + 1):
if number % i == 0: # 如果存在能整除number的因子,则不是质数
return False
return True # 没有因子,说明是质数
# 将列表分为奇数和偶数
def separate_odd_even(numbers: list[int]) -> tuple[list[int], list[int]]:
odd_numbers = [] # 存放奇数
even_numbers = [] # 存放偶数
for number in numbers:
if number % 2 != 0: # 判断奇数
odd_numbers.append(number)
else: # 判断偶数
even_numbers.append(number)
return odd_numbers, even_numbers # 返回奇数和偶数列表
# 生成奇数和偶数的匹配矩阵
def create_prime_matrix(odds: list[int], evens: list[int]) -> list[list[int]]:
prime_matrix = [[0 for _ in evens] for _ in odds] # 初始化矩阵
for i, odd in enumerate(odds):
for j, even in enumerate(evens):
if is_prime(odd + even): # 如果奇数和偶数之和为质数
prime_matrix[i][j] = 1 # 标记为1
return prime_matrix # 返回匹配矩阵
# 深度优先搜索匹配
def find_match(x: int, prime_matrix: list[list[int]], used_evens: list[int], matches: list[int]) -> bool:
for j in range(len(prime_matrix[0])): # 遍历偶数列表
if prime_matrix[x][j] == 1 and not used_evens[j]: # 如果匹配且未访问
used_evens[j] = 1 # 标记为已访问
# 如果偶数没有匹配或者递归找到新的匹配
if matches[j] == -1 or find_match(matches[j], prime_matrix, used_evens, matches):
matches[j] = x # 记录匹配的奇数
return True
return False # 没有找到匹配
# 主程序
while True:
try:
num_items = int(input()) # 读取整数n
items = list(map(int, input().split())) # 读取n个整数并转换为列表
odd_numbers, even_numbers = separate_odd_even(items) # 分离奇数和偶数
prime_matrix = create_prime_matrix(odd_numbers, even_numbers) # 生成匹配矩阵
matches = [-1] * len(even_numbers) # 初始化匹配数组
match_count = 0
for i in range(len(odd_numbers)):
used_evens = [0] * len(even_numbers) # 初始化访问标记
if find_match(i, prime_matrix, used_evens, matches): # 尝试为当前奇数找到匹配
match_count += 1 # 匹配成功计数加1
print(match_count) # 输出匹配数量
except:
break # 捕获异常并退出循环
匈牙利算法
即图论中寻找最大匹配的算法,暂不考虑加权的最大匹配(用KM算法实现)。主要用于解决一些与二分图匹配有关的问题。
匈牙利算法主要用来解决两个问题:求二分图的最大匹配数和最小点覆盖数。