Official

D - Vacation Together Editorial by Nyaan


はじめに、問題をいくつかのパートに分解してみましょう。
まず、この問題を解くには、部分問題として「\(j\) 日目は全員が暇な日ですか?」という問題を \(j=1, 2, \dots, D\) について解く必要があります。これは各 \(j\) について「\(i=1,2,\dots,N\) について \(S_{i, j}\) は全て o か?」という問題が解ければよく、for-loop 等を利用して走査すれば \(\mathrm{O}(ND)\) で求まります。

部分問題の答えを文字列 \(T\) に記録したとします。 (\(T\)\(j\) 文字目が o のときは全員暇, x の時はそうでない) すると、次の問題の答えが元の問題の答えとなります。

長さ \(D\) の文字列 \(T\) があります。 o が連続している個数は最大で何個ですか?

これは以下に説明するアルゴリズムで \(\mathrm{O}(D)\) で高速に計算できます。(典型問題なので解法を覚えておくとよいでしょう)

  • o が連続している個数の最大値を記録する変数を \(\mathrm{ans}\) とする。はじめ \(\mathrm{ans} = 0\) である。
  • また、(文字列を左から見ていくとして) 今まで見た文字列の末尾に連続する o の個数を \(\mathrm{cur}\) とする。はじめ \(\mathrm{cur} = 0\) である。
  • \(i=1, 2, \dots, D\) について次の操作を行う。
    • \(T[i] =\) o である場合は、今まで見た文字列の末尾の o が 1 個増えるので \(\mathrm{cur}\)\(\mathrm{cur} + 1\) に更新する。
    • \(T[i] =\) x である場合は、末尾が x になるので、\(\mathrm{cur}\)\(0\) に更新する。
  • その後、\(\mathrm{ans}\)\(\max(\mathrm{ans}, \mathrm{cur})\) に更新する。

ほかにもやり方はありますが、ともすれば計算量が \(\mathrm{O}(D^2)\)\(\mathrm{O}(D^3)\) となり制約が大きい条件下では TLE する低速なアルゴリズムになってしまうのに注意しましょう。(この問題では \(D \leq 100\) なので問題ありません。)

以上よりこの問題を \(\mathrm{O}(ND)\) で解くことができて、十分高速です。

  • C++ による実装例(この実装例では \(T\) を実際に構築せずに部分問題を1 回ずつ解きながら \(\mathrm{ans}, \mathrm{cur}\) の値を更新しています。)
#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main() {
  int N, D;
  cin >> N >> D;
  vector<string> S(N);
  for (auto& s : S) cin >> s;

  int ans = 0, cur = 0;
  for (int j = 0; j < D; j++) {
    int can = 1;
    for (int i = 0; i < N; i++) {
      can &= S[i][j] == 'o';
    }
    if (can) {
      cur++;
    } else {
      cur = 0;
    }
    ans = max(ans, cur);
  }
  cout << ans << "\n";
}

posted:
last update: