算法交流|【博弈题】翻转游戏
6245
2022.02.09
2022.02.09
发布于 未知归属地

游戏规则如下:
给你一个字符串 currentState ,其中只含 '+''-' 。你和朋友轮流将 连续 的两个 "++" 反转成 "--" 。你是先手玩家。
当一方无法进行翻转时便意味着游戏结束,另一方获胜。默认每个人都会采取最优策略。

请你判定你自己是否存在必胜的方案 :如果存在,返回 true ;否则,返回 false 。

输入:currentState = "++++"
输出:true
解释:你作为先手玩家,可将中间的 "++" 翻转变为 "+--+" ,从而获胜。
数据范围 : 1 <= currentState.length <= 60

这题我想了半天,想到一种 可能的 动态规划的办法,但是苦于技术菜,没法完整实现自己的想法(也可能是因为我的想法本身就是错误的),也没法证明它是否正确。

所以想请教各位。😂😂

我的想法是这样的:如果一段连续的 加号数量 是确定的,那么这一段的结果(谁获胜)也是确定的。

比如,我已知的初始情况是 : +++++++++ 。这些情况都一定是当前先手玩家获胜。

如果是连续4个以上的加号,可以每次减2(因为每次会翻转两个),最终 这个连续长度 会变为某个 已知结果的长度
此外,在每次减2的过程中,还要用一个布尔变量来维护先后手玩家的顺序(不断取反)。


以上就是我的想法,我也给它写了一些代码,但我感觉自己既不能完整实现这个想法的逻辑,也不能证明它的正确性,全凭直觉。

Java
public class Solution {
  public boolean canWin(String currentState) {
    if (currentState.length() == 1) {
      return false;
    }
    // 以 '-' 作为分隔符,可以将字符串分为一段段连续的 '+'
    // len[i] -> 第i段连续的 '+' 的长度
    int[] len = new int[61];  // 61是由于数据范围 1<=currentState.length<=60,所以直接开辟了一个61的
    int left = 0, pointer = 0;
    for (int right = 0, N = currentState.length(); right < N; right++) {
      if (currentState.charAt(right) == '-') {
        if (left < right) {
          len[pointer++] = right - left + 1;
        }
        left = right + 1;
      }
    }
    if (left == 0) {
      // 整个字符串中不存在 '-' ,即:整个字符串是一个连续的 '+'
      len[pointer++] = currentState.length();
    }
    // 维护当前先后手玩家的关系。如果当前回合是后手玩家的,那么记为 false
    boolean reversed = false;
    // dp[i] -> 先手玩家能否在长度为i的连续'+' 中必胜
    Boolean[] dp = new Boolean[61];
    // change[i] -> 在翻转完毕长度为i的连续段之后,是否会导致先后手的顺序互换
    // 例如:"++++" 则先手翻转一次,然后不能再翻转,这会导致下一轮的先手是当前的后手玩家
    boolean[] change = new boolean[61];
    dp[1] = false;
    dp[2] = dp[3] = dp[4] = true;
    change[2] = change[3] = change[4] = true;
    // 处理每一段连续的 '+'
    for (int i = 0; i < pointer; i++) {
      if (dp[len[i]] == null) {
        // 当前长度谁赢是不确定的。但是可以通过不断减2,直到变为某个确定的长度,由此推出答案
        int length = len[i];
        while (length > 4 && dp[length] == null) {
          length -= 2;
          reversed = !reversed;
        }
        dp[len[i]] = !reversed && dp[length - 2];
        change[len[i]] = change[length - 2];
      } else {
        reversed = change[len[i]] && !reversed;
      }
    }
    return !reversed && dp[len[pointer - 1]];
  }
}
评论 (14)