問題
整数屋さんでは 以上 以下の整数が売られている。 整数 の値段は を10進法表記したときの各位の和を とするとき、 円である。
所持金が 円のとき、購入可能な最大の整数を求めよ。
入力
1行に整数 が順に与えられる。
条件
出力
答えとなる整数を1行に出力すること。
解法
「整数の大きい方から順に を計算し、初めて 以下であるもの」を探索していくという方法がまずは考えられます。 しかし、考える整数の範囲は 以上 以下のすべてであるので、値によっては実行時間制限に間に合いません。
ここで、各整数によって の値が異なってくることから、 の値を固定することを考えます。
固定した値を とすると、求める整数 については \begin{align} a \times n + b \times d \leq x \end{align} が成立するので、 \begin{align} n \leq \frac{x - b \times d}{a} \end{align} が言えます。
従って、この右辺の値以下の整数で、 であるものの最大値を探索することにより、このときの答えを求めることができます。
ここで、 としても、特に問題はありません。 というのも、 のとき、 \begin{align} a \times n + b \times d(n) \leq a \times n + b \times d \leq x \end{align} が成立するためです。 以上から、先程の不等式の右辺の値以下の整数のうち、 であるものの最大値を探索すれば、 であるものの最大値を求めることができます。
従って、次に を整数として、 かつ である の最大値を求める方法を考えます。 整数の最大値としては であることから、これは以下の手順によって求められます。
- 「現在の数値」として を、「現在の桁数」として を、「残りの桁の和」として を保持する
- から までの整数 について、大きい方から順に以下の条件を同時に満たすものを探索する
- が「残りの桁の和」以下である
- 「(現在の数値)(現在の桁数)」が 以下である
- 上記の条件を満たす整数 を用いて、以下のように数値を更新する
- 「現在の数値」を「(現在の数値)(現在の桁数)」に更新する
- 「現在の桁数」を「(現在の桁数)」に更新する
- 「残りの桁の和」を「(残りの桁の和)」に更新する
- 手順2, 3を「現在の桁数」が になるまで繰り返す
以上の手順を踏むことにより、「 以下になるようにしながら、上の方の桁に大きい数字を詰めていく」ということが可能になります。 また、手順2, 3を繰り返す回数は多くて10回程度で、手順2でのループ回数も最大で10回なので、 の値が与えられれば、最大値も 程度で求めることができます。
さて、この問題では売られている整数は 以上 以下であるので、 の値として考えられる最大値は のときの となります。
従って、 のすべての整数 について、整数 の値を求め、以上の手順を踏むことにより、 程度の計算量で整数の最大値を求めることができます。
ソースコード
まずは、 かつ である整数 の最大値を求める関数 solve()
を以下のように実装しました。
ll solve(ll digit_sum, ll max_value) { // digit_sum が d(桁の和)、max_value が y (最大値)を表す ll ans = 0, digit = ll(1e9); // ans は最終的に導かれる最大値を、digit は「現在の桁数」をそれぞれ表す while (digit > 0) { // 9 から 0 の順で条件を満たすか見る( i = 0 は必ず条件を満たす) for (ll i = 9; i >= 0; i--) { if (i <= digit_sum && ans + i * digit <= max_value) { digit_sum -= i; // 条件を満たす i を用いて、桁の和を引く ans += i * digit; // 条件を満たす i を用いて、答えを更新する break; } } digit /= 10; // 現在の桁数から 10 を割る } return ans; }
この solve()
が実装されている状態で、 main()
関数の中に答えを出力する部分を実装しました。
int main() { ll a, b, x; cin >> a >> b >> x; // 値の入力 ll ans = 0; // 答えとなる値 // 桁の和が 1 から 81 の場合のすべてについて、考えられる最大値を順に求めていく for (ll i = 1; i <= 81; i++) { ll rest = (x - b * i) / a; // 現在の値に対しての最大値 (y) を求める if (rest <= 0) { // もし0以下になる場合は、買える整数が無いので次の値について見る continue; } ans = max(ans, solve(i, min(rest, ll(1e9)))); // 買える整数の最大は 10^9 であるので、引数に注意して値を更新する } cout << ans << endl; // 答えの出力 return 0; }