番兵を入れるとかすれば、怖いケースをあらかじめ除去できそう
問題概要
左右方向一列に 個のマスが並んでいます。
この 個のマスのうち、マス の 個のマスは青色で、それ以外のマスは白色です。
あなたは一回だけ、正整数 を一つ選んで幅 のハンコを作ります。幅 のハンコを一回使用すると、 個のマスのうち連続する マスを選び、それらを赤色に塗り替えることができます。ただしその際、その 個のマスの中に青色のマスが入っていてはなりません。
とハンコの使用方法をうまく決めた時、最小で何回ハンコを使用すれば、白色のマスが存在しない状態にすることができるでしょうか。
制約
考えたこと
下図のように、
- 白いところの塊に分けて
- その長さの最小値を とする (これより長いと青マスに被ってしまう)
というふうにして、
- 白マスにスタンプを押していく
とすれば OK。
実装するときには、さらに下図のように、両端に青いマスを追加して考えるとわかりやすくなる。こういうのを「番兵」といったりする。
あと、青いマスが連続しているところを「長さ 0 の白塊」として処理しないように注意!!! 「長さ 0 の白塊」は除外して考えないといけない!!!
コーナーケース
以下のケースがちょっと怖い
- 全部が「青」
- 「青」が 1 個もない
実装次第だけど、これらは気にしなくてもいいようにできる。下の実装では、全部が「青」という場合は、ベクトル v が空になるだけで答えは結局 0 になる。
「青」が 1 個もないというケースは、番兵さえ入れておけば関係ない。
コード
計算量は を小さい順に並び替える必要性から、 となる。
#include <bits/stdc++.h> using namespace std; int main() { long long N, M; cin >> N >> M; vector<long long> A(M); for (int i = 0; i < M; ++i) cin >> A[i]; sort(A.begin(), A.end()); // 番兵追加 A.insert(A.begin(), 0); A.push_back(N+1); // 白塊に分解 (同時に最小値も求める) vector<long long> v; long long K = N; for (int i = 0; i+1 < A.size(); ++i) { long long len = A[i+1] - A[i] - 1; if (len > 0) { v.push_back(len); K = min(K, len); } } long long res = 0; for (auto len : v) res += (len + K - 1) / K; cout << res << endl; }