blog

1日1ビッグリート(整数分割) 難易度:中級~上級

正の整数 n が与えられたとき,それを少なくとも2つの正の整数の和に分割し, これらの整数の積を最大にしなさい. 得られる最大の積を返しなさい. nは2以上58以下と仮定してよい....

Aug 24, 2020 · 2 min. read
シェア

正の整数 n が与えられたとき、それを少なくとも2つの正の整数の和に分割し、それらの整数の積を最大化しなさい。 得られる最大の積を返しなさい.

例題:

  1. 例1
 : 2
 : 1
 : 2 = 1 + 1, 1 × 1 = 1 
  1. 例2
 : 10
 : 36
 :  + 4, 3 × 3 × 4 = 36 

内容:

nは2以上58以下と仮定してください。

推論

n個の数字に分割され、nは固定されていない このタイプの問題:ルールは確実、開始条件は不確定。

  • 動的計画法
  • 再帰

動的計画法

nを列挙し、dpは異なるnの結果値を記録し、dp[n]はクエリー結果:

  • 分割する整数の数を決定:<=i<=n2<=i<=n
  • dp[i]はiで分割された整数の積の最大値。
    • 2つ以上のj,dp[i - j]を分割 -> dp[i - j]は2つ、つまりより多くの部分集合を分割
    • 2つのjの分割,i-j
/** * @param {number} n * @return {number} */ var integerBreak = function (n) { let dp = Array(n + 1).fill(1) for (let i = 2; i <= n; i++) { let curMax = 0 for (let j = 1; j < i; j++) { curMax = Math.max(curMax, Math.max(j * (i - j), j * dp[i - j])) } dp[i] = curMax } return dp[n] }

再帰

  • パラメータで分割する整数を指定します。
  • 終了条件:
    • 既に分割された配列に遭遇した場合

再帰のロジックは基本的にダイナミック・プログラミングと同じです:

  • 数字jを分割
  • i-jに属する数字は、単独で数字として成り立つことも、分割され続けることもできます。
/** * @param {number} n * @return {number} */ var integerBreak = function (n) { let dp = Array(n + 1).fill(1) function dfs(n){ if (dp[n]) return dp[n]; let curMax = 0; for (let i = 1; i < n; i++) { curMax = Math.max(curMax, i * (n - i), i * dfs(n - i)); } return dp[n] = curMax; }; return dp[n] }

最適化された動的計画法

/** * @param {number} n * @return {number} */ var integerBreak = function (n) { if (n < 4) return n - 1; let dp = Array(n + 1).fill(1) for (let i = 3; i <= n; i++) { dp[i] = Math.max(Math.max(2 * (i - 2), 2 * dp[i - 2]), Math.max(3 * (i - 3), 3 * dp[i - 3])); } return dp[n]; }

ブログ:

出版:The Pit's Little Bookie

Read next

アクティビティの起動プロセス

その後、親クラスが呼び出され、Activityのメソッドが呼び出され、アプリケーションが新しいタスクスタックに引き上げられたことを示すマーカーが追加されます。 注目すべきは

Aug 24, 2020 · 29 min read