题解 | #火眼金睛#

火眼金睛

https://www.nowcoder.com/practice/d311403b15b8495b81b622edaefd5b5a

解题思路:模拟

本题中,被判定为作弊有两种方式:即(a)互相回答问题判定;(b)提问被2个以上作弊者回答。整体思路是:先找到所有(a)类作弊者,再一步步找出(b)类作弊者。

使用user_to_user,users,questions三个字典,以及集合ans,它们的用途如下:

  • user_to_user:记录每个用户 id 回答过谁的问题。用于快速找出(a)类作弊者
  • questions:记录每个问题的提问者、以及回答者中已经被标记为作弊的用户数量。当被标记次数加至2时,提问者判定为(b)类作弊者
  • users:记录每个id回答过问题的序号。用于发现新的作弊者后,快速定位并标记其回答过的问题。
  • ans:记录已经被标记为作弊者的id。

模拟的过程如下:

1. 对于输入的每个问题,找出提问者 q 和若干个回答者。然后:

1.1. 对于每个回答者 x,将问题的序号 i 放入 users[x] 中

1.2. 如果 q 已经回答过 x 的问题,他们在第一轮同时判定为作弊。否则将 x 放入 user_to_user[q] 中

1.3. 初始化 questions 中的问题 i :记录下提问者 q ,被标记答者数初始化为 0

2. 第一轮被标记为作弊的 id 已经被记录在 ans 中。可以开始第二轮标记。具体做法是:对于每个作弊者 x 和其回答过的每个问题 q,问题 q 被标记一次。然后如果这是 q 第二次被标记,那么 q 的提问者将在第二轮被判定为作弊

3. 重复 2 的过程,直到没有新的作弊者被判定为作弊。

from collections import defaultdict
def solve():
    ans = set()
    user_to_user = defaultdict(set) # i提问被谁回答过
    users = defaultdict(list)       # id, questions
    questions = {}                  # id: [user, cheater_count]
    for i in range(int(input())):
        ipt = [int(x) for x in input().split()]
        q, a = ipt[0], set(ipt[2:])
        for x in a:                 # i:问题序号,q: 提问者,x:回答者
            users[x].append(i)
            if q in user_to_user[x]:
                ans |= {q, x}
            elif q != x:
                user_to_user[q].add(x)
        questions[i] = [q, 0]
    s = ans
    while s:
        ss = set()      # 下轮即将更新的作弊者
        for x in s:
            for q in users[x]:
                questions[q][1] += 1     # cheater_count 增加 1
                if questions[q][1] == 2:
                    ss.add(questions[q][0])
        ans |= ss
        s = ss
    print(len(ans))
    if ans:
        print(str(list(ans)).replace(',','')[1:-1])

while 1:
    try:
        solve()
    except EOFError:
        break

全部评论

相关推荐

1 收藏 评论
分享
牛客网
牛客企业服务