C++で解く:デジタル時計の数字列を0にする最小操作回数を求めるアルゴリズム
問題概要
n桁の数字列 S があるとします。この S はデジタル時計を表しており、文字列全体で 0 から 10n − 1 までの整数を表示しています。桁数が足りない場合は、先頭に 0 が並んで表示されるものとします。行える操作は次の2種類です。
時計の表示する数値を 1 減らす
任意の2つの桁を入れ替える(スワップする)
できるだけ少ない操作回数で、時計の表示を 0 にしたいと考えています。そのために必要な最小の操作回数を求めましょう。
たとえば、入力が S = "1000" の場合、出力は 2 になります。先頭の 1 と末尾の 0 を入れ替えて "0001" にし、続けて 1 減らす操作を行えば "0000" にできるためです。
アプローチ
「1 減らす」操作では、繰り下がりが発生しても、実際に値が変化するのは最も右側にある 0 以外の桁だけです。そのため、末尾以外の位置にある 0 以外の各桁は、スワップで末尾へ移動させてから 0 まで減らすのが最適な戦略になります。
この性質を利用すると、答えは次の要素の合計として求められます。
- 全桁の数字の合計値:各桁を 0 にするために必要な「1 減らす」操作の回数
- 末尾以外にある 0 以外の桁の個数:各桁を末尾へ移動させるために必要なスワップの回数(1桁につき1回)
手順
以下の手順に従って解くことができます。
n := S の長さ
x := S の末尾の桁の値
i を 0 から n−2 まで 1 ずつ増やしながら繰り返す:
もし S[i] が '0' でなければ:
x := x + (S[i] の数値) + 1
x を返す
C++実装例
理解を深めるために、次の実装を見てみましょう。
#include <bits/stdc++.h>
using namespace std;
int solve(string S) {
int n = S.size();
int x = S[n - 1] - '0';
for (int i = 0; i <= n - 2; i++)
if (S[i] != '0')
x = x + S[i] + 1 - '0';
return x;
}
int main() {
string S = "1000";
cout << solve(S) << endl;
}
入力
"1000"
出力
2
計算量
文字列を一度走査するだけなので、時間計算量は O(n)、追加の記憶領域をほとんど使わないため空間計算量は O(1) となります。非常に効率的な解法です。
-
【Python】文字列をソート済みにするまでの最小操作回数を求めるアルゴリズム
問題の概要 文字列 s が与えられます。この文字列に対して、昇順に並んだ「ソート済みの文字列」になるまで、以下の一連の操作を繰り返し適用します。 ステップ1: 1 ≤ i < len(s) を満たし、かつ s[i] < s[i - 1] となる最大のインデックス i を選びます。 ステップ2: i ≤ j < len(s) を満たし、範囲 [i, j] に含まれるすべての k について s[k] < s[i - 1] が成り立つ最大のインデックス j を選びます。 ステップ3: インデックス i - 1 と j の位置にある2つの文字を入れ替えます。 ステップ4: イ
-
Pythonで1つの数を別の数に変換するのに必要な最小操作回数を求めるプログラム
問題の概要 2つの整数 start と end(start < end)が与えられます。次の2種類の操作のみを使って start を end に変換するとき、必要な操作の最小回数を求めるプログラムを作成しましょう。 数値に 1 を加える(インクリメント) 数値に 2 を掛ける 例として、start = 5、end = 11 の場合を考えます。5 に 2 を掛けて 10 とし、そこへ 1 を加えれば 11 になるため、答えは 2 回となります。 解き方のアプローチ この問題は、start から順に操作を試すよりも、end から逆算していく貪欲法(グリーディ法)が有効です。end が偶