华为OD机试_2025 B卷_猜数字(Python,100分)(附详细解题思路)

题目描述

一个人设定一组四码的数字作为谜底,另一方猜。

每猜一个数,出数者就要根据这个数字给出提示,提示以XAYB形式呈现,直到猜中位置。

其中X表示位置正确的数的个数(数字正确且位置正确),而Y表示数字正确而位置不对的数的个数。

例如,当谜底为8123,而猜谜者猜1052时,出题者必须提示0A2B。

例如,当谜底为5637,而猜谜者才4931时,出题者必须提示1A0B。

当前已知N组猜谜者猜的数字与提示,如果答案确定,请输出答案,不确定则输出NA。

输入描述
第一行输入一个正整数,0<N < 100。

接下来N行,每一行包含一个猜测的数字与提示结果。

输出描述
输出最后的答案,答案不确定则输出NA。

用例

输入6
4815 1A1B
5716 0A1B
7842 0A1B
4901 0A0B
8585 3A0B
8555 2A1B
输出3585
说明

猜数字游戏解题思路(确定谜底数字)

一、核心解题思路

问题分析
题目要求根据多组猜测和对应的XAYB提示(X表示数字和位置都正确的个数,Y表示数字正确但位置不对的个数),确定唯一的四位数谜底:

  1. 输入包含N组(猜测数字, XAYB提示)
  2. 谜底是四位数(取值范围1000~9999)
  3. 需要验证候选谜底是否满足所有提示
  4. 输出规则:
  • 唯一满足条件的谜底 → 输出该数字
  • 多个满足条件 → 输出"NA"
  • 无满足条件 → 输出"NA"

关键策略

  1. 候选谜底生成:遍历1000~9999所有四位数
  2. 提示验证:对每个候选谜底检查是否满足所有XAYB提示
  3. XAYB计算
  • 计算数字和位置都正确的数量(A)
  • 计算数字正确但位置错误的数量(B)
  1. 结果筛选:记录所有满足条件的谜底,根据数量决定输出
二、完整代码实现
def calculate_A_B(candidate, guess):
    """计算候选谜底与猜测的A和B值"""
    candidate_str = str(candidate)
    guess_str = str(guess)
    
    # 计算A(位置和数字都正确)
    A = 0
    for i in range(4):
        if candidate_str[i] == guess_str[i]:
            A += 1
    
    # 计算B(数字正确但位置错误)
    candidate_digits = {}
    guess_digits = {}
    
    # 统计未匹配位置的数字频率
    for i in range(4):
        if candidate_str[i] != guess_str[i]:
            candidate_digits[candidate_str[i]] = candidate_digits.get(candidate_str[i], 0) + 1
            guess_digits[guess_str[i]] = guess_digits.get(guess_str[i], 0) + 1
    
    B = 0
    for digit in guess_digits:
        if digit in candidate_digits:
            B += min(guess_digits[digit], candidate_digits[digit])
    
    return A, B

def main():
    import sys
    data = sys.stdin.read().splitlines()
    if not data:  # 处理空输入
        print("NA")
        return
        
    n = int(data[0])
    guesses = []
    
    # 解析输入数据
    for i in range(1, n + 1):
        if i >= len(data):  # 确保数据行存在
            break
        parts = data[i].split()
        if len(parts) < 2:  # 确保有猜测和提示
            continue
            
        guess_num = parts[0]
        hint = parts[1]
        # 确保提示格式正确
        if len(hint) != 3 or hint[1] != 'A' or not hint[0].isdigit() or not hint[2].isdigit():
            continue
            
        # 提取A和B的值
        A_val = int(hint[0])
        B_val = int(hint[2])
        guesses.append((guess_num, A_val, B_val))
    
    solutions = []
    
    # 遍历所有可能的四位数候选
    for candidate in range(1000, 10000):
        candidate_str = str(candidate)
        valid = True
        
        # 检查是否满足所有猜测条件
        for guess_num, A_val, B_val in guesses:
            A, B = calculate_A_B(candidate_str, guess_num)
            if A != A_val or B != B_val:
                valid = False
                break
        
        if valid:
            solutions.append(candidate_str)
    
    # 输出结果
    if len(solutions) == 0:
        print("NA")
    elif len(solutions) == 1:
        print(solutions[0])
    else:
        print("NA")

if __name__ == "__main__":
    main()
三、算法原理解析

1. A值计算

A = 0
for i in range(4):
  if candidate[i] == guess[i]:
A += 1
  • 直接比较相同位置的数字
  • 完全匹配时计数增加

2. B值计算

# 统计未匹配位置的数字频率
for i in range(4):
  if candidate[i] != guess[i]:
     candidate_digits[candidate[i]] += 1
     guess_digits[guess[i]] += 1

# 计算共同数字的最小频率
B = 0
for digit in guess_digits:
   if digit in candidate_digits:
      B += min(guess_digits[digit], candidate_digits[digit])
  • 排除已匹配位置(A值位置)
  • 统计剩余数字的出现频率
  • 取相同数字的最小频率作为B值

3. 候选验证
对每个候选谜底:

  1. 遍历所有猜测提示
  2. 计算候选谜底与当前猜测的A、B值
  3. 与提示的A、B值比对
  4. 任意提示不匹配即淘汰该候选
四、示例解析

输入示例

6
4815 1A1B
5716 0A1B
7842 0A1B
4901 0A0B
8585 3A0B
8555 2A1B

验证过程(以候选3585为例):

  1. 4815 → 计算:A=1(第4位5匹配),B=1(8在候选第3位) → 匹配1A1B
  2. 5716 → 计算:A=0,B=1(5在候选第2,4位) → 匹配0A1B
  3. 7842 → 计算:A=0,B=1(8在候选第3位) → 匹配0A1B
  4. 4901 → 计算:A=0,B=0 → 匹配0A0B
  5. 8585 → 计算:
  • A=3(第2位5、第3位8、第4位5匹配)
  • B=0 → 匹配3A0B
  1. 8555 → 计算:
  • A=2(第2位5、第4位5匹配)
  • B=1(8在候选第3位) → 匹配2A1B

结果输出:3585

图解说明

候选谜底:3 5 8 5

猜测4815:
3≠4, 5≠8, 8≠1, 5=5 → A=1
未匹配:候选[3,5,8], 猜测[4,8,1] → 共同数字8 → B=1

猜测5716:
3≠5, 5=5(匹配A不参与B), 8≠1, 5≠6 → A=0
未匹配:候选[3,8,5], 猜测[7,1,6] → 无共同数字 → B=0(但实际计算有5?注意匹配的5已排除)
修正:候选未匹配[3,8,5]中的5在候选第4位(未匹配位置),猜测中未匹配的7,1,6 → 无共同 → B=0?
但题目提示0A1B → 矛盾?

重新计算5716:
候选:3 5 8 5
猜测:5 7 1 6
位置1:3≠5 → 未匹配
位置2:5=5 → 匹配(A=1?但题目提示0A)
位置3:8≠1 → 未匹配
位置4:5≠6 → 未匹配
因此A=1(位置2)≠0 → 不符合0A1B

问题:题目实际输入是5716 0A1B,但候选3585在位置2匹配(5),A应该为1

但实际运行结果3585符合所有条件,说明题目中提示是准确的。重新检查题目:

"第二组:5716 -> 0A1B
3 vs 5 -> 不同
5 vs 7 -> 不同
8 vs 1 -> 不同
5 vs 6 -> 不同 -> A=0
候选未匹配位置:3,5,8,5 (注意:谜底3585,所以未匹配位置是全部:3,5,8,5,频率:3:1,5:2,8:1)
猜测未匹配位置:5,7,1,6
5:在频率表中(出现2次),所以B=1,然后频率表中5变成1次(5:1)。
7:不在频率表中,跳过。
1:不在,跳过。
6:不在,跳过。
所以B=1,提示0A1B符合。"

关键点:位置2的5在候选中是5,在猜测中也是5,但为什么算未匹配?
因为题目要求"位置正确"才计入A,位置2的候选是5,猜测是7?

实际猜测5716:
位置1:5(猜测) vs 3(候选) → 不匹配
位置2:7(猜测) vs 5(候选) → 不匹配
位置3:1(猜测) vs 8(候选) → 不匹配
位置4:6(猜测) vs 5(候选) → 不匹配
所以A=0,不是1

先前错误认为猜测是"5716"对应位置2是5,实际是7

结论:3585满足所有条件
五、总结

关键要点

  • A值计算:位置和数字完全匹配
  • B值计算
  • 排除已匹配位置
  • 频率统计共同数字
  • 取最小值保证不重复计数
  • 结果筛选
  • 唯一解 → 输出数字
  • 多解/无解 → 输出"NA"

应用场景

  • 猜数字游戏AI
  • 密码破解系统
  • 数据验证工具
  • 游戏测试自动化

该解法通过系统枚举和精确验证,高效解决了猜数字游戏的谜底确定问题,能正确处理各种边界情况和重复数字场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值