小Q和小X的游戏 | 豆包MarsCode AI刷题

133 阅读6分钟

小Q和小X的游戏 - MarsCode

小Q和小X的游戏

问题描述

小Q和小X是很好的朋友,她们正在玩一个游戏。她们拿到了一个数组,游戏开始时小Q随机选择一个元素作为起点。接着,两人轮流行动,小Q先行动。

每次行动时,当前玩家需要选择当前元素左边比它更小的元素,然后移动到该元素,接下来换另一方从这个元素继续移动。如果某一方无法进行合法的移动,则该方输掉游戏。

小Q想知道,在双方都采取最优策略的情况下,她最终获胜的概率是多少?请输出分数的最简形式,即分子和分母互素。如果小Q必胜,则输出 1/1。如果小Q必败,则输出 0/1


测试样例

样例1:

输入:n = 5,a = [3, 1, 5, 4, 3]
输出:'3/5'

样例2:

输入:n = 6,a = [6, 2, 9, 7, 4, 3]
输出:'2/3'

样例3:

输入:n = 4,a = [8, 5, 6, 3]
输出:'1/4'

思路

  1. 博弈模型分析

    • 对于每个数组中的元素 a[i],我们可以定义一个状态 dp[i],表示从 i 这个位置出发,小Q是否能够必胜:

      • 如果从 i 位置出发,小Q有至少一个可以合法移动的目标位置 j(满足 a[j] < a[i]),并且从 j 出发后,轮到小X时小X必败,那么 dp[i] = True
      • 如果从 i 位置出发,小Q无论如何移动,都会让小X进入一个必胜的位置,则 dp[i] = False
  2. 动态规划的状态转移

    • 从位置 i 出发,小Q可以选择的合法目标是所有比 a[i] 小的元素的位置。对于每一个目标位置 j(满足 a[j] < a[i]),如果从 j 出发,小X会输,则小Q可以必胜。因此,我们要检查每一个合法的目标位置,并通过 dp 数组来更新当前状态。
  3. 游戏的结果

    • 小Q在每个位置的胜负是基于该位置的 dp[i] 值来决定的。如果小Q从某个位置出发能必胜,则这个位置是小Q的胜利位置;否则,小X能必胜。
  4. 胜率计算

    • 对于数组中的每个位置 i,检查从该位置出发小Q是否能必胜,最终统计出小Q能必胜的位置数量,并计算其概率。
    • 结果应该以最简分数的形式输出。

步骤

  1. 计算所有可能的状态:遍历数组的每一个位置,检查该位置上的合法移动。
  2. 动态规划:计算每个位置上是否小Q必胜。
  3. 概率计算:计算从任意位置出发小Q必胜的概率。

代码实现

python
复制代码
from math import gcd

def solution(n, a):
    # dp[i] 表示从位置 i 出发,小Q是否必胜
    dp = [False] * n
    
    # 从后往前处理每个位置
    for i in range(n - 1, -1, -1):
        for j in range(i - 1, -1, -1):
            if a[j] < a[i]:  # 如果 a[j] 比 a[i] 小,且可以从 a[i] 移动到 a[j]
                if not dp[j]:  # 如果从 j 出发小X必败,那么从 i 出发小Q必胜
                    dp[i] = True
                    break
    
    # 统计小Q必胜的位置数量
    win_count = sum(dp)
    
    # 分子和分母
    numerator = win_count
    denominator = n
    
    # 计算最大公约数
    common_divisor = gcd(numerator, denominator)
    
    # 输出最简分数
    return f'{numerator // common_divisor}/{denominator // common_divisor}'

def main():
    n = int(input())  # 读取数组长度
    a = list(map(int, input().split()))  # 读取数组
    print(solution(n, a))

# 调用主函数
if __name__ == "__main__":
    main()

解释

  1. dp 数组

    • dp[i] 表示从位置 i 出发,小Q是否能必胜。如果 dp[i] = True,说明从 i 位置开始,小Q能够必胜;否则小Q必败。
  2. 动态规划填充

    • 我们从数组的后往前填充 dp 数组。对于每个位置 i,我们检查它左边的所有合法移动,如果存在一个位置 j(满足 a[j] < a[i]),并且从位置 j 出发小X必败,那么从位置 i 出发小Q必胜。
  3. 统计小Q必胜的概率

    • 统计所有 dp[i] = True 的位置数,即小Q能够必胜的位置数量 win_count
    • 然后,输出 win_count / n 的最简分数形式。使用 gcd 函数来简化分数。

示例

示例 1:

输入:

复制代码
5
3 1 5 4 3

输出:

复制代码
3/5

解释:

  • 小Q可以从第 1、2、4 位置出发获胜,因此小Q获胜的概率是 3/5。

示例 2:

输入:

复制代码
6
6 2 9 7 4 3

输出:

复制代码
2/3

解释:

  • 小Q可以从第 1、2、3、4、5 位置出发,但只有 2/3 的位置小Q能获胜。

示例 3:

输入:

复制代码
4
8 5 6 3

输出:

复制代码
1/4

解释:

  • 只有从第 1 位置出发,小Q能获胜,其他位置小Q必败。

时间复杂度

  • 由于我们对于每个位置 i 都要检查它左边的所有位置 j,所以时间复杂度是 O(n^2),其中 n 是数组的长度。

空间复杂度

  • 我们使用一个大小为 ndp 数组,因此空间复杂度是 O(n)

这个解法在较小的 n 下表现良好,如果 n 很大,可以考虑进一步优化。

最后

这个问题本质上是一个博弈问题,涉及到小Q和小X交替行动。游戏规则是:每个玩家需要选择一个比当前元素小的元素作为移动目标,若无法选择合法目标,则该玩家输掉游戏。我们需要通过动态规划(DP)来判断在每个位置出发时,小Q是否能必胜。

首先,我们定义 dp[i] 表示从位置 i 开始,小Q是否能必胜。如果 dp[i]True,表示小Q能在该位置必胜;如果 dp[i]False,则表示小Q必败。

我们从数组的最后一个元素开始反向处理,对于每个位置 i,检查其左侧是否有一个合法的目标位置 j,使得从 j 开始小X必败。若存在这样的 j,则从 i 开始小Q必胜,dp[i] 设置为 True

最后,我们统计所有 dp[i]True 的位置,计算小Q必胜的概率,并将其输出为最简分数形式。

时间复杂度是 O(n^2),适用于中等规模的输入。