【C++】長さNの数字列を、いずれかの1桁がK回以上出現するように変換する方法
はじめに
このチュートリアルでは、長さNの数値を変換して、いずれか1つの数字(0〜9)が少なくともK回含まれるようにするプログラムを、C++を用いて解説します。
問題の概要
長さNの数値(数字列)が与えられます。私たちのタスクは、与えられた数値のいくつかの桁を書き換えて、どれか1つの数字が少なくともK回繰り返されるようにすることです。同時に、この操作にかかるコストも計算する必要があります。ここでいうコストとは、元の数字と書き換え後の数字の差の絶対値の合計のことです。最終的に、コストが最小となる変換後の数値と、その最小コストを出力します。
アルゴリズムの考え方
この問題は、目標となる数字を0〜9のそれぞれについて試す貪欲法で解くことができます。手順は以下の通りです。
- まず、対象の数字が元の数値に既に何個含まれているかを数えます。
- 必要な個数Kに足りない場合は、対象の数字との差が小さい数字(差1、差2…)から順に、置き換えの候補を探します。
- 対象より大きい数字は左端側から、対象より小さい数字は右端側から置き換えることで、変換後の数値をできるだけ小さく保ちます。
- 各候補について得られたコストと変換後の数値を比較し、全体で最小となる組み合わせを出力します。
C++での実装例
#include <bits/stdc++.h>
using namespace std;
// 最小コストと最終的な数値を計算する関数
int get_final(int n, int k, string a){
int modtemp; // 変更にかかったコストの合計
int co; // 目標の数字への変更数
string temp; // 変換途中の数値
// 最小コストとそのときの数値を保持
pair<int, string> ans = make_pair(INT_MAX, "");
for (int i = 0; i < 10; i++) {
temp = a;
modtemp = 0;
co = count(a.begin(), a.end(), i + '0'); // 既に出現している個数をカウント
for (int j = 1; j < 10; j++) {
if (i + j < 10) {
// 目標より大きい数字を目標の数字へ置き換え(左端から走査)
for (int p = 0; p < n; p++) {
if (co <= k)
break;
if (i + '0' == temp[p] - j) {
temp[p] = i + '0';
modtemp += j;
co++;
}
}
}
if (i - j >= 0) {
// 目標より小さい数字を目標の数字へ置き換え(右端から走査)
for (int p = n - 1; p >= 0; p--) {
if (co >= k)
break;
if (i + '0' == temp[p] + j) {
temp[p] = i + '0';
modtemp += j;
co++;
}
}
}
}
// 以前の最小コストより小さければ更新
ans = min(ans, make_pair(modtemp, temp));
}
cout << ans.first << endl << ans.second << endl;
}
int main(){
int n = 5, k = 4;
string a = "21122";
get_final(n, k, a);
return 0;
}
出力
1 21222
出力の解説
n = 5、k = 4、入力文字列「21122」の場合、先頭の「1」を「2」に書き換えると、数字「2」が4回出現する数値「21222」が得られます。このときのコストは |1 − 2| = 1 となり、これが最小です。したがって、プログラムは最小コスト「1」と変換後の数値「21222」を出力します。
まとめ
本記事では、長さNの数値を、いずれか1桁がK回以上出現するように変換し、その最小コストを求めるC++プログラムを紹介しました。0〜9の各数字を目標として全探索し、近い数字から順に置き換えていくのがポイントです。計算量はO(10 × 9 × N)程度に収まるため、Nがある程度大きくなっても高速に動作します。
-
C++ですべての要素を割り切れる配列の要素を見つける方法
いくつかの要素を持つ配列 A があるとします。この中から「他のすべての要素を割り切ることができる」1つの要素を見つけたいと思います。例として、配列 A = [15, 21, 69, 33, 3, 72, 81] を考えてみましょう。この場合、答えは 3 になります。リスト内のすべての数値が3で割り切れるためです。解決策のアプローチこの問題は、以下の手順でシンプルに解くことができます。まず、配列内の最小値を求めます。次に、すべての要素がその最小値で割り切れるかどうかを確認します。すべて割り切れれば、その最小値を返します。1つでも割り切れない要素があれば、-1 を返します(条件を満たす要素は存在し
-
C++で「x + 桁の合計 = n」を満たす数xを見つける方法
この記事では、ある整数 n が与えられたとき、「x + x の各桁の合計 = n」という条件を満たす数 x を求める問題を解説します。例として、n = 21 の場合を考えてみましょう。このとき答えは x = 15 となります。なぜなら、15 の各桁の合計は 1 + 5 = 6 であり、15 + 6 = 21 となって、与えられた n と一致するからです。解き方のアプローチこの問題はシンプルな方法で解くことができます。1 から n まで順番に数を調べていき、それぞれの数について「その数自身 + 各桁の合計」が n と等しくなるかどうかを確認します。条件を満たす数が見つかった時点で処理を終了し、そ