D - Swap to Gather 解説
by
yuto1115
解説
いくつかの解法が考えられますが、ここでは実装が最も簡単である(と思われる)ものを解説します。少々形式的な説明が多くなるため、最初に直観的な説明を載せておきます。
直観的な説明
$S$ に含まれる各0 について、それより左にある 1 の個数($l$ とおく)とそれより右にある 1 の個数($r$ とおく)を考える。$l< r$ のとき、その 0 は 1 のかたまりの左側に持ってくる方がお得。逆に $l\geq r$ のとき、その 0 は 1 のかたまりの右側に持ってくる方がお得。よって、答えは $\min\{l,r\}$ をすべての 0 について足し合わせたものとなる。
\(S\) に含まれる 0 の個数を \(C_0\)、1 の個数を \(C_1\ (=N-C_0)\) とおきます。
各 \(i\ (1\leq i\leq C_0)\) について、\(d_i\) を「左から \(i\) 番目の 0 よりも左にある 1 の個数」と定義します。また、便宜上 \(d_0=0, d_{C_0+1}=C_1\) と定義します。このとき、\(d_0 \leq d_1\leq d_2\leq \dots \leq d_{C_0}\leq d_{C_0+1}\) です。
\(d_i\) の値に着目して本問題を捉え直してみましょう。まず、同じ文字同士を入れ替える操作には意味がないことから、元の問題における操作は、\(d_i\) に対して以下のいずれかの操作を行うことと同値です。
- \(d_{i-1}<d_i\) なる \(i\ (1\leq i\leq C_0)\) を一つ選び、\(d_i\) を \(1\) 減少させる。
- \(d_i<d_{i+1}\) なる \(i\ (1\leq i\leq C_0)\) を一つ選び、\(d_i\) を \(1\) 増加させる。
また、終了条件は以下と同値です。
- ある \(p\ (0\leq p\leq C_0)\) が存在し、\(0=d_1=\dots=d_p\) かつ \(d_{p+1}=\dots=d_{C_0}=C_1\)
一回の操作では \(1\) つの \(d_i\) の値が \(1\) だけ変化することから、
\[K=\displaystyle\sum_{i=1}^{C_0} \min\{d_i, C_1-d_i\}\]
と定義すると、\(K\) 回以上の操作が明らかに必要です。
逆に、\(d_i\) が単調増加であることから、\(K\) 回の操作で終了条件を満たすことが常に可能なため、答えは \(K\) です。(\(d_i < C_1-d_i\) なる最大の \(i\) (存在しなければ \(0\)) を \(p\) とすればよいです。)
実装例 (C++) :
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
string s;
cin >> n >> s;
int c1 = 0;
for (char c: s) {
if (c == '1') ++c1;
}
int now = 0;
long long ans = 0;
for (char c: s) {
if (c == '0') {
ans += min(now, c1 - now);
} else {
++now;
}
}
cout << ans << endl;
}
投稿日時:
最終更新: