小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'
思路
-
博弈模型分析:
-
对于每个数组中的元素
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。
- 如果从
-
-
动态规划的状态转移:
- 从位置
i出发,小Q可以选择的合法目标是所有比a[i]小的元素的位置。对于每一个目标位置j(满足a[j] < a[i]),如果从j出发,小X会输,则小Q可以必胜。因此,我们要检查每一个合法的目标位置,并通过dp数组来更新当前状态。
- 从位置
-
游戏的结果:
- 小Q在每个位置的胜负是基于该位置的
dp[i]值来决定的。如果小Q从某个位置出发能必胜,则这个位置是小Q的胜利位置;否则,小X能必胜。
- 小Q在每个位置的胜负是基于该位置的
-
胜率计算:
- 对于数组中的每个位置
i,检查从该位置出发小Q是否能必胜,最终统计出小Q能必胜的位置数量,并计算其概率。 - 结果应该以最简分数的形式输出。
- 对于数组中的每个位置
步骤
- 计算所有可能的状态:遍历数组的每一个位置,检查该位置上的合法移动。
- 动态规划:计算每个位置上是否小Q必胜。
- 概率计算:计算从任意位置出发小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()
解释
-
dp 数组:
dp[i]表示从位置i出发,小Q是否能必胜。如果dp[i] = True,说明从i位置开始,小Q能够必胜;否则小Q必败。
-
动态规划填充:
- 我们从数组的后往前填充
dp数组。对于每个位置i,我们检查它左边的所有合法移动,如果存在一个位置j(满足a[j] < a[i]),并且从位置j出发小X必败,那么从位置i出发小Q必胜。
- 我们从数组的后往前填充
-
统计小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是数组的长度。
空间复杂度
- 我们使用一个大小为
n的dp数组,因此空间复杂度是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),适用于中等规模的输入。