りんご購入最適化問題

動的プログラミングを用いた詳細解析と可視化

問題概要
目的

3種類のりんごセット(x個/a円、y個/b円、z個/c円)を使って、n個以上のりんごを最小コストで購入する

制約条件
  • 1 ≤ n ≤ 1,000
  • 1 ≤ x < y < z ≤ 1,000
  • 1 ≤ a < b < c ≤ 10,000
  • 購入したりんごがn+1個以上になってもよい
時間計算量: O(n × max(x,y,z)) 空間計算量: O(n + max(x,y,z))
JavaScript実装コード
minCostForApples.js
/**
 * n個以上のりんごを最小コストで購入するための金額を計算する
 * 動的プログラミングを使用して効率的に解を求める
 */
function minCostForApples(n, x, a, y, b, z, c) {
    // 効率的な上限を設定(計算量とメモリ使用量を最適化)
    const maxApples = n + Math.max(x, y, z) - 1;
    
    // DPテーブルを初期化(Int32Arrayの範囲内でINFを設定)
    const INF = 2147483647; // Int32Arrayの最大値
    const dp = new Int32Array(maxApples + 1);
    dp.fill(INF);
    dp[0] = 0; // 0個の場合はコスト0
    
    // 動的プログラミングで最小コストを計算
    for (let i = 0; i <= maxApples; i++) {
        if (dp[i] === INF) continue;
        
        const currentCost = dp[i];
        
        // セット1(x個でa円)を使う場合
        if (i + x <= maxApples) {
            dp[i + x] = Math.min(dp[i + x], currentCost + a);
        }
        
        // セット2(y個でb円)を使う場合
        if (i + y <= maxApples) {
            dp[i + y] = Math.min(dp[i + y], currentCost + b);
        }
        
        // セット3(z個でc円)を使う場合
        if (i + z <= maxApples) {
            dp[i + z] = Math.min(dp[i + z], currentCost + c);
        }
    }
    
    // n個以上のりんごを手に入れる最小コストを求める
    let minCost = INF;
    for (let i = n; i <= maxApples; i++) {
        if (dp[i] < minCost) {
            minCost = dp[i];
        }
    }
    
    return minCost;
}
動的プログラミングの処理流れ
Step 1: 初期化

dp[0] = 0(0個のりんごを手に入れるコストは0円)

他の全ての要素はINFで初期化

Step 2: 状態遷移

各状態 i から3つのセット購入を試行:

  • dp[i+x] = min(dp[i+x], dp[i] + a)
  • dp[i+y] = min(dp[i+y], dp[i] + b)
  • dp[i+z] = min(dp[i+z], dp[i] + c)
Step 3: 最適解の探索

dp[n]からdp[maxApples]までの最小値を求める

実行例解析 (n=9, x=2/a=100, y=3/b=125, z=5/c=200)
DPテーブルの変化過程
個数 0 2 3 4 5 6 7 8 9 10 11 12 13
初期 0
i=0後 0 100 125 200
i=2後 0 100 125 200 200 225 300
i=3後 0 100 125 200 200 225 250 300 325 250
最終 0 100 125 200 200 225 250 300 325 250 350 350 375
最適解の導出過程
セット選択肢の比較
  • セット1のみ: 2個×5回 = 10個, 500円
  • セット2のみ: 3個×3回 = 9個, 375円 ✓
  • セット3のみ: 5個×2回 = 10個, 400円
  • 混合パターン: より高コスト
結果

最小コスト: 375円 (セット2を3回購入)

メモリ効率化のポイント
Int32Array使用の利点
  • 通常のArray比で約50%のメモリ削減
  • 高速なメモリアクセス
  • 固定サイズによる予測可能な性能
注意事項

INF値は2147483647(2³¹-1)に設定してオーバーフローを防止

詳細アルゴリズム実行ログ
アルゴリズム複雑度分析
時間計算量: O(n × max(x,y,z))

外側ループ: O(n + max(x,y,z)) ≈ O(n)
内側処理: O(1) × 3回の遷移チェック
総計算量: O(n × max(x,y,z))

空間計算量: O(n + max(x,y,z))

DPテーブル: O(n + max(x,y,z))のInt32Array
補助変数: O(1)
総メモリ使用量: O(n + max(x,y,z))

最適化のポイント
  • Int32Arrayによる高速メモリアクセス
  • INF値の適切な設定でオーバーフロー回避
  • 早期終了条件による不要な計算の削減
  • キャッシュフレンドリーな順次アクセスパターン
パフォーマンス分析
実行時間 vs 問題サイズ
ベンチマーク結果
テストケース実行結果
n 結果 時間(ms) メモリ(KB)
テストを実行してください
学習リソースと参考資料
動的プログラミングの理論
  • 最適部分構造: 部分問題の最適解から全体の最適解を構築
  • 重複する部分問題: 同じ部分問題を何度も解く必要性
  • メモ化: 計算結果をテーブルに保存して再利用
実装のベストプラクティス
  • 適切なデータ構造の選択(TypedArray vs 通常のArray)
  • 境界値の適切な処理
  • オーバーフロー対策
  • メモリ効率とパフォーマンスのバランス
類似問題
  • コイン問題(Coin Change Problem)
  • ナップサック問題の変形
  • 最小コスト経路問題
  • 組み合わせ最適化問題