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\) に更新する。
- \(T[i] =\)
- その後、\(\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: