C#のボトムアップ方式で「1にする最小ステップ数」を実装する方法
問題の概要:「1にする最小ステップ数」とは
「1への最小ステップ数(Minimum Steps to One)」は、動的計画法(DP)の入門問題としてよく知られています。整数 n が与えられ、次の3種類の操作を繰り返し適用して、n を 1 に到達させるまでの最小回数を求めます。
- 操作1: n が 3 で割り切れるとき、n を 3 で割る
- 操作2: n が 2 で割り切れるとき、n を 2 で割る
- 操作3: 常に実行可能。n から 1 を引く
ボトムアップ方式(Tabulation)の考え方
ボトムアップ方式では、再帰呼び出しを行わずに、小さな部分問題から順番に答えを計算し、dp 配列に結果を保存していきます。dp[i] は「i を 1 にするために必要な最小ステップ数」を表します。
アルゴリズムの手順
- 整数 n を入力として受け取ります。
- 初期条件として、n が 1 の場合は 0 を返します。
- 候補値 op1・op2・op3 を最大値(int.MaxValue)で初期化します。
- i が 3 で割り切れる場合は、op1 に dp[i / 3] を代入します。
- i が 2 で割り切れる場合は、op2 に dp[i / 2] を代入します。
- op3 には常に dp[i - 1] を代入します。
- dp[i] = min(op1, op2, op3) + 1 として最小ステップ数を記録します。
- すべての計算が終わったら、dp 配列から最終的な値を返します。
C#での実装例
public class DynamicProgramming{
public int MinimumStepstoOne(int n){
int[] dp = new int[100];
dp[1] = 0;
for (int i = 2; i <= n; i++){
int op1 = int.MaxValue, op2 = int.MaxValue, op3 = int.MaxValue;
if (i % 3 == 0){
op1 = dp[i / 3];
}
if (i % 2 == 0){
op2 = dp[i / 2];
}
op3 = dp[i - 1];
dp[i] = Math.Min(Math.Min(op1, op2), op3) + 1;
}
return dp[n];
}
}
static void Main(string[] args){
DynamicProgramming dp = new DynamicProgramming();
Console.WriteLine(dp.MinimumStepstoOne(10));
}
実行結果
3
なぜ「n = 10 で 3 ステップ」になるのか
n = 10 の場合、最短経路は次のようになります。
10 → 9(−1)→ 3(÷3)→ 1(÷3)
このように、単純に 1 を引き続けるだけでは 4 ステップ以上かかるところを、割り算をうまく組み合わせることで 3 ステップに短縮できます。これこそが動的計画法による最適化のポイントです。
計算量
時間計算量: O(N) ― 各 i に対して定数回の比較と配列参照しか行わないためです。
空間計算量: O(N) ― 中間結果を保存するための dp 配列が必要になるためです。
-
Java 9でJavaFXを使ってJShellをプログラムから実装する方法
JShellは、サンプル式を対話的に実行できるツールです。通常はコマンドラインから利用しますが、JavaFXアプリケーションの中でプログラム的にJShellを実装することも可能です。その場合、Javaプログラムに以下のパッケージをインポートする必要があります。import jdk.jshell.JShell; import jdk.jshell.SnippetEvent; import jdk.jshell.VarSnippet;これらのクラスの役割は次のとおりです。JShell:評価エンジンの本体。式や文を評価(eval)するためのAPIを提供します。SnippetEvent:評価結果として
-
Javaでスタックを使ってキュー(Queue)を実装する方法を解説
キューとスタックの基本Queue(キュー)は Collection インターフェースを継承したクラスで、FIFO(First-In-First-Out:先入れ先出し)方式による要素の挿入と削除をサポートします。一方、Stack(スタック)は Vector クラスのサブクラスであり、LIFO(Last-In-First-Out:後入れ先出し)方式でオブジェクトを管理します。つまり、スタックの一番上に追加された最後の要素が、最初に取り出される要素になります。この2つのデータ構造の性質は正反対ですが、スタックを2つ組み合わせることで、キューを実装することが可能です。以下では、その具体的な実装方法を紹