SRM 701 Div 2 Hard : ThueMorseGame

TopCoder Statistics - Problem Statement

通せなくてめちゃくちゃ悔しい。

当初の解法

DPだなって思ってしまった。

  public class ThueMorseGame {
    boolean[][] dp;
    boolean[][] done;
    int n;
    int m;

    public String get(int n, int m) {
      this.n = n;
      this.m = m;
      dp = new boolean[n + 1][2];
      done = new boolean[n + 1][2];
      return dfs(n, 0) ? "Alice" : "Bob";
    }

    boolean dfs(int remain, int turn) {
      if (done[remain][turn]) {
        return dp[remain][turn];
      }
      if (isLose(remain)) {
        done[remain][turn] = true;
        dp[remain][turn] = true;
        return dp[remain][turn];
      }
      if (remain <= m) {
        done[remain][turn] = true;
        dp[remain][turn] = true;
        return dp[remain][turn];
      }
      boolean flag = false;
      for (int i = m; 1 <= i; --i) {
        int nr = remain - i;
        flag |= dfs(nr, 1 - turn);
      }
      done[remain][turn] = true;
      dp[remain][turn] = flag;
      return dp[remain][turn];
    }

    boolean isLose(int num) {
      int cnt = 0;
      while (num > 0) {
        if (num % 2 == 1) {
          ++cnt;
        }
        num /= 2;
      }
      return cnt % 2 == 1;
    }
  }

このあとメモリー使いすぎ問題のため、
byteにしてビットで管理するも、
そもそも遷移にO(m)かかっているので、
O(mn)でダメになる。

解法を見て

https://apps.topcoder.com/wiki/display/tc/SRM+701

ビット演算??? 意味分からね~~~

となってけど、読み砕いてみる。

まず上記のソースについて、その数字が勝てる数字なのか
どうかしか着目しなくていいので、[2]がいらない。
あと、これをfor文のDPに書き直す。

    public String get(int n, int m) {
      boolean[] dp = new boolean[n + 1];
      for (int i = 1; i <= n; ++i) {
        if (i <= m) {
          dp[i] = true;
        } else if (isLose(i)) {
          dp[i] = true;
        } else {
          boolean flag = false;
          for (int j = 1; j <= m; ++j) {
            int nr = i - j;
            flag |= dp[nr];
          }
          dp[i] = flag;
        }
      }
      return dp[n] ? "Alice" : "Bob";
    }

O(m)の遷移が大変邪魔なので、
これをbitで管理する。

    public String get(int n, int m) {
      boolean[] dp = new boolean[n + 1];
      long bits = 0;
      for (int i = 1; i <= n; ++i) {
        if (i <= m) {
          dp[i] = true;
        } else if (isLose(i)) {
          dp[i] = true;
        } else {
          boolean flag = ((~bits) & full) > 0 ;
          dp[i] = flag;
        }
        bits = (bits << 1L) | (dp[i] ? 1L : 0L);
      }
      return dp[n] ? "Alice" : "Bob";
    }

ビットを反転させているのは、
その数を取ったら相手が勝つ遷移というのに
行かないようにするため。
相手が負ける遷移が勝ち。

そもそもdpテーブルがいらないことに
気づく。
あと、isLose関数が遅かった(なぜ?)
ので、Integer.bitCountを利用する。

  public class ThueMorseGame {
    public String get(int n, int m) {
      long bits = 0;
      long full = (1L << m) - 1L;
      for (int i = 1; i <= n; ++i) {
        long win = 0;
        if (i <= m) {
          win = 1;
        } else if (isLose(i)) {
          win = 1;
        } else {
          win = ((~bits) & full) > 0 ? 1 : 0;
        }
        bits = (bits << 1L) | win;
      }
      return (bits & 1) == 1 ? "Alice" : "Bob";
    }

    boolean isLose(int num) {
      return Integer.bitCount(num) % 2 == 1;
    }
  }

それでもだいぶ遅いんですが・・・。
でもこれでSystem Testに通ることができた。