刷题 No203 小Q和小X的游戏
问题描述
小Q和小X是很好的朋友,她们正在玩一个游戏。她们拿到了一个数组,游戏开始时小Q随机选择一个元素作为起点。接着,两人轮流行动,小Q先行动。
每次行动时,当前玩家需要选择当前元素左边比它更小的元素,然后移动到该元素,接下来换另一方从这个元素继续移动。如果某一方无法进行合法的移动,则该方输掉游戏。
小Q想知道,在双方都采取最优策略的情况下,她最终获胜的概率是多少?请输出分数的最简形式,即分子和分母互素。如果小Q必胜,则输出 1/1。如果小Q必败,则输出 0/1。
问题分析
这道题的关键是分析游戏的状态,并通过动态规划和博弈论来求解。我们可以将问题看作一个“博弈”问题,其中每个元素表示一个“游戏状态”。两位玩家轮流从当前状态中选择一个更小的元素进行移动,直到某一方无法继续移动为止。要解决这个问题,我们需要判断每个状态(即数组中的每个元素)是否是“赢”的状态还是“输”的状态,最终通过动态规划得到小Q的胜率。
解题思路
1、状态定义:
- 定义每个元素为一个状态,数组中每个位置的元素都对应一个可能的游戏状态。
- 小Q和小X轮流选择一个比当前元素小的元素作为下一步的移动目标。如果当前元素的左边没有比它小的元素可选,那么当前玩家就输掉了。
2、胜负状态判断
- 每个状态有两种可能:胜利状态(Win)和失败状态(Lose)。当轮到玩家进行决策时,如果他能选择一个使对方必败的状态,那么当前状态就是胜利状态。反之,如果每个选择都会让对方进入胜利状态,则当前状态就是失败状态。
3、动态规划
- 设
dp[i]表示从数组第i个位置开始,当前玩家能否必胜。如果dp[i] = true,表示从位置i开始当前玩家必胜;如果dp[i] = false,表示从位置i开始当前玩家必败。 - 状态转移:对于每个位置
i,如果存在一个位置j(在i之前且nums[j] < nums[i])使得dp[j] = false,即可以强制对方进入一个必败状态,那么dp[i] = true。 - 初始状态:如果当前位置没有比它小的元素,那么
dp[i] = false,即当前玩家必败
- 最后:统计所有小Q能胜的初始位置,然后输出胜率。
示例代码
public static String solution(int n, int[] a) {
// dp数组,dp[i]表示从位置i出发当前玩家是否必胜
boolean[] dp = new boolean[n];
// 反向遍历数组,从后往前处理dp值
for (int i = n - 1; i >= 0; i--) {
// 判断当前位置是否有比当前元素小的左边元素
boolean canWin = false;
for (int j = i - 1; j >= 0; j--) {
if (a[j] < a[i] && !dp[j]) {
// 如果可以跳到一个对方必败的状态
canWin = true;
break;
}
}
dp[i] = canWin;
}
// 计算小Q的胜率
int winCount = 0;
for (int i = 0; i < n; i++) {
if (dp[i]) {
winCount++;
}
}
// 输出最简分数
int gcd = gcd(winCount, n);
String result = winCount / gcd + "/" + n / gcd;
return result;
}
// 求最大公约数
public static int gcd(int a, int b) {
while (b != 0) {
int temp = a % b;
a = b;
b = temp;
}
return a;
}