Codeforces第 941 轮[A]Everything Nim题解

题目详情:

A. Everything Nim

Alice and Bob are playing a game on 𝑛𝑛 piles of stones. On each player's turn, they select a positive integer 𝑘 that is at most the size of the smallest nonempty pile and remove 𝑘𝑘 stones from each nonempty pile at once. The first player who is unable to make a move (because all piles are empty) loses.

Given that Alice goes first, who will win the game if both players play optimally?

Input

The first line of the input contains a single integer 𝑡𝑡 (1≤𝑡≤104) — the number of test cases. The description of the test cases follows.

The first line of each test case contains a single integer 𝑛 (1≤𝑛≤2⋅10^5) — the number of piles in the game.

The next line of each test case contains 𝑛𝑛 integers 𝑎1,𝑎2,…𝑎𝑛】 (1≤𝑎𝑖≤10^9), where 𝑎𝑖𝑎𝑖 is the initial number of stones in the 𝑖-th pile.

It is guaranteed that the sum of 𝑛𝑛 over all test cases does not exceed 2⋅10^5.

Output

For each test case, print a single line with the name of the winner, assuming both players play optimally. If Alice wins, print "Alice", otherwise print "Bob" (without quotes).

中文的大致意思:

这个题目描述了Alice和Bob在一个游戏中玩石头堆的游戏。游戏规则是这样的:有n堆石头,每一轮玩家选择一个正整数k,这个k不能超过所有非空石头堆的石头数量,然后每一堆石头都会被减去k个石头。先不能进行操作的玩家输掉游戏。

现在问题是,如果Alice先手,假设两位玩家都采取最优策略,谁会赢得游戏?

输入
输入的第一行包含一个整数t(1≤t≤10^ 4)——测试用例的数量。接下来是每个测试用例的描述。

每个测试用例的第一行包含一个整数n(1≤n≤2⋅1^5)——游戏中石头堆的数量。

接下来每个测试用例的第二行包含n个整数a1,a2,…an(1≤ai≤10^9),其中ai是第i堆石头的初始数量。

保证所有测试用例中的n总和不超过2⋅10^5。

输出
对于每个测试用例,打印一行,表示获胜者的名字,假设两位玩家都采取最优策略。如果Alice获胜,则打印“Alice”,否则打印“Bob”。

解题思路

这道题的难处应该就是“假设两位玩家都采取最优策略”,那么怎么实现这个最优策略就是关键。

1.初始步骤:
   如果最小堆的大小为1,Alice必须选择𝑘=1,因此我们可以假设Bob先走,并从所有堆中减去1,然后再轮到Alice。
   重复这个过程,直到没有剩余的牌堆或者最小堆的大小不再为1。

2. 最小堆大小为𝑥≥2时:
   第一个玩家总是会获胜。因为无论第一个玩家选择𝑘=𝑥还是𝑘=𝑥−1,都能使得下一个玩家处于失败状态,从而第一个玩家继续获胜。

   a.如果第一个玩家选择 k = x:

  • 这样第一步之后所有的石头堆的块数减去x
  • 下一个玩家面临的情况是每个堆的石头数量都至少减少了𝑥-1个,因为每个堆中至少有一个石头被减去了。也就是说,最小堆的大小至少为1。
  • 因为最小堆的大小至少为1,下一个玩家必须选择𝑘=1,而剩余的每个堆的石头数量都至少为1。这样,第一个玩家可以轻松地获胜。

   b.如果第一个玩家选择k = x -1:

  • 这样第一步之后,每个石头堆的数目都减去x-1
  • 下一个玩家面临的情况就是,每个堆的石头数量都至少减少了x-2,因为每个堆中都至少有一个石头被减去了。也就是说,最小堆的大小至少为2
  • 下一个玩家面临的情况是最小堆大小至少为2,这意味着下一个玩家无法达到失败状态。然后,第一个玩家可以继续选择合适的𝑘来确保下一个玩家进入失败状态,并最终获胜。

3. 找出MEX:
   为了找出不是堆大小的最小正整数𝑏,我们可以跟踪最大的堆大小𝑎,然后将所有堆的大小放入一个集合中。

   MEX表示Minimum EXcluded(最小排除数),即不是当前集合中的最小正整数。

   在这个问题中,我们将所有石头堆的大小放入一个集合中,并且我们知道了最大的堆大小𝑎。我们希望找到不在这个集合中的最小正整数𝑏。

现在考虑两种情况:

  1. 如果𝑏>𝑎:这意味着在所有石头堆的大小中,都存在比最大堆的大小还要大的数。因此,无论Alice还是Bob都无法继续操作,因为无法选择比某个堆中的石头数量还要大的数。游戏将在这一步结束,并且获胜者取决于最大堆的奇偶性。如果最大堆的大小为偶数,则Bob获胜,如果为奇数,则Alice获胜。

  2. 否则,如果𝑏≤𝑎:这意味着我们最终会达到至少一个堆大小为2的状态,因为𝑏不在当前堆大小的集合中,所以我们需要取出最小的堆。在这种情况下,游戏将继续进行,而𝑏的奇偶性将决定最终的获胜者。如果𝑏为偶数,Bob将获胜,如果为奇数,Alice将获胜。

4. 复杂度分析:
   这个算法的时间复杂度为𝑂(𝑛)或𝑂(𝑛log𝑛),取决于实现方式。在找出MEX时,如果使用哈希集合,时间复杂度为O(n),如果使用排序,时间复杂度为O(nlogn)。

通过这种算法,我们可以确定在给定石头堆数量和初始数量的情况下,谁将获胜。

代码

def find_mex(piles):
    # 将所有石头堆的大小放入一个集合中
    pile_set = set(piles)
    # 初始化最大堆大小为集合中的最大值
    max_pile = max(pile_set)

    # 遍历1到最大堆大小+1的范围,寻找不在集合中的最小正整数
    for i in range(1, max_pile + 2):
        if i not in pile_set:
            return i  # 返回MEX


def find_winner(t, test_cases):
    winners = []
    for piles in test_cases:
        mex = find_mex(piles)  # 找到MEX
        if mex > max(piles):  # 如果MEX大于最大堆大小
            # Alice或Bob的获胜取决于最大堆的奇偶性
            winners.append("Alice" if max(piles) % 2 == 1 else "Bob")
        else:
            # 否则,MEX的奇偶性决定了获胜者
            winners.append("Alice" if mex % 2 == 1 else "Bob")
    return winners


# 读取输入
t = int(input())
test_cases = []
for _ in range(t):
    _ = int(input())  # 不需要读取石头堆的数量n,因为只需要石头堆的大小
    piles = list(map(int, input().split()))
    test_cases.append(piles)

# 找到获胜者并输出结果
winners = find_winner(t, test_cases)
for winner in winners:
    print(winner)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值