学習コンテンツテック企業求人ブログ面接対策サポート

Coding InterviewCat

トップ

01 Coding InterviewCat

はじめに

02 イントロダクション03 Coding InterviewCat対象読者

コーディング面接対策とロードマップ

04 企業ごとの対策のレベル感05 コーディング面接に対する心構え06 コーディング面接対策ロードマップ

Python基礎と計算量

07 コーディング面接で必要なPythonの学習08 計算量とBig O

Discordサポートについて

09 Discordサポート(購入者特典)

本書掲載のLeetCode問題集

10 本書に掲載されているLeetCode問題集

配列 / 文字列

11 配列と文字列(導入)12 ハッシュテーブル(導入)13 ソート(導入)14 スタック(導入)15 配列 / 文字列(基礎)二重ループ16 配列 / 文字列(基礎)ハッシュテーブル17 配列 / 文字列(基礎)ソート, カスタムソート, バケットソート18 配列 / 文字列(基礎)行列 2D Matrix19 配列 / 文字列(基礎)スタック20 配列 / 文字列(応用)累積和(Prefix Sum)21 配列 / 文字列(応用)Two Pointers22 配列 / 文字列(応用)Sliding Window23 配列 / 文字列(応用)In-place Counting, Negative Marking24 配列 / 文字列(応用)Quickselect

ヒープ / 優先度付きキュー

25 ヒープ / 優先度付きキュー(導入)26 ヒープ / 優先度付きキュー(基礎) heapify, heappush, heappop27 ヒープ / 優先度付きキュー(基礎)ヒープソート

再帰呼び出し / バックトラック法

28 再帰呼び出し / バックトラック法(導入)29 再帰呼び出し / バックトラック法(基礎)再帰30 再帰呼び出し / バックトラック法(応用)バックトラック

連結リスト

31 連結リスト(導入)32 連結リスト(基礎)リスト走査33 連結リスト(基礎)ノード削除34 連結リスト(基礎)リスト反転35 連結リスト(基礎) 複数のリスト走査36 連結リスト(応用) Two Pointers, Slow/Fast Pointers37 連結リスト(応用) 双方向リスト38 キュー(導入)

二分探索

39 二分探索(基礎)値の探索, 境界の探索40 二分探索(基礎)下界, 上界41 二分探索(応用)答えの決めうち二分探索, 最長部分増加列42 二分探索(発展)2D 最長部分増加列

二分木

43 二分木(導入)44 二分木(基礎)BFS, DFS45 二分木(基礎)巡回, 二分探索木46 二分木(応用)二分木の再構築, 二分木のシリアライズ

グラフ

47 グラフ(導入)48 グラフ(基礎)BFS, DFS49 グラフ(基礎)二次元配列50 グラフ(基礎)ダイクストラ51 グラフ(基礎)トポロジカルソート52 グラフ(応用)木の直径, 強連結成分, 関節点 & 橋53 グラフ(応用)Unionfind, 最小全域木54 グラフ(応用)Warshall-Floyd, 0-1 BFS55 グラフ(発展)グラフDP

動的計画法

56 動的計画法(導入)57 動的計画法(基礎)貰うDP, 配るDP58 動的計画法(基礎)”まで”を状態として扱う, 状態の拡張59 動的計画法(基礎)二次元状態DP60 動的計画法(応用)グラフDP, メモ化再帰DP61 動的計画法(応用)辞書で状態を管理, bitで状態を管理62 動的計画法(応用)2つのDP, 絶対値DP, ゲームDP63 動的計画法(発展)スタックとDP, 累積和とDP
64 [Coming Soon] Bit Manipulation65 [Coming Soon] 貪欲法66 [Coming Soon] トライ木、サフィックス木67 [Coming Soon] Intervals68 [Coming Soon] 数学
© 2026 InterviewCat. All rights reserved.
プライバシーポリシー利用規約特定商取引法に基づく表記運営お問い合わせフォーム
  1. 学習コンテンツ
  2. Coding InterviewCat
  3. 動的計画法(基礎)貰うDP, 配るDP

動的計画法(基礎)貰うDP, 配るDP

LeetCode 練習問題集

問題
難易度
重要度
テクニック
Climbing Stairs
★★
高
貰うDP, 配るDP
Min Cost Climbing Stairs
★★
高
貰うDP, 配るDP
DPには2種類あり、それぞれ貰うDP、配るDPと呼ばれます。問題を解く際にはどちらで解くかを意識する必要があります。本章ではそれぞれでやっていることと、違いを解説します。
筆者個人としては貰うDPの方が理解しやすいかと思うので、先にこちらを解説していきます。

貰うDP

先ほどのフィボナッチ数の問題を貰うDPを使用して解いてみましょう。

例題. Fibonacci Number(フィボナッチ数)

難易度: ★ 重要度: 高
—
N番目のフィボナッチ数を求めてください。N番目のフィボナッチ数例は以下のように1つ前(N-1番目)と2つ前(N-2番目)の数の和で求められます。ただしN = 0, N = 1のときは1とします。

解説

再帰関数だと以下の図のように分岐していき、最終的にの計算量で無駄が非常に多いことを導入編では解説しました。
notion image
DPでは一度求めた値を記録することでこの無駄を無くします。これはとてもシンプルでmemoという配列にメモすることで実現できます。つまりmemo[i]がi番目のフィボナッチ数を格納します。これによりmemo[N] = memo[N-1] + memo[N-2]と配列の値を見るだけでmemo[N]を求めることができます。そしてこのmemo[N]がまさしく求めたいN番目のフィボナッチ数です。
この様にある状態を求める際に他の既に求まった状態の値を貰って計算していることから貰うDPとなります。実際に後ほど説明する配るDPと比較することでより理解が深まるかと思います。

ソースコード

(1)では他の既に求まった状態の値(memo[i-1]とmemo[i-2])を貰って未知のmemo[i]を計算しています。

計算量(BigO)

  • Time Complexity : Nまでfor文を回しているだけです。
  • Space Complexity : memoに使用しています。
実はメモリ計算量をさらに改善することが可能です。これはフォローアップ問題として聞かれることが多いので、試しに解いてみましょう。
以下がメモリ計算量を改善したソースコードとなります。この様に、1つ前と2つ前のフィボナッチ数のみ必要なので実はの定数メモリで解くことができます。
貰うDP、配るDPどちらでも解ける練習問題を最後においていますので、ここでは練習問題は無いです。

配るDP

それでは同様に配るDPで以下のフィボナッチ数の問題を解いてみましょう。

例題. Fibonacci Number(フィボナッチ数)

難易度: ★ 重要度: 高
N番目のフィボナッチ数を求めてください。N番目のフィボナッチ数例は以下のように1つ前(N-1番目)と2つ前(N-2番目)の数の和で求められます。ただしN = 0, N = 1のときは1とします。

解説

貰うDPと同じ様にmemo配列を使用して一度求めた値を記録することで計算の無駄を無くします。ただし、配るDPではi番目のフィボナッチ数が求まったタイミングで、これを将来的には使用して計算する今はまだ未知のi+1、i+2番目のフィボナッチ数に渡します。ソースコード(1)の様にある状態が求まったら、その求まった値を使うであろう未知の状態に配るため配るDPといいます。

ソースコード

フィボナッチ数の問題では圧倒的に貰うDPの方が直感的に書きやすいです。ただ、問題によっては配るDPも使う必要があるのでどちらも使える様にしておきましょう。

計算量(BigO)

  • Time Complexity : Nまでfor文を回しているだけです。
  • Space Complexity : memoに使用しています。
この例題はメモリ計算量の解き方を考え無くて良いです。一般的にメモ(memo)を設定しない方がコード自体は複雑になります。元々この問題と相性が悪い配るDPでメモリ計算量を実現する意味が余り無いです。
⚠️
貰うDPと配るDPは日本語では競技プログラミングの界隈では通じますが、一般的な単語としてエンジニア全体で認知されているかは微妙です。しかし、この2種類があることを理解していることは問題を解く際には非常に重要になってきます。なのでDPの問題を解くときはどちらで解いているかを意識しましょう。 また貰うDPと配るDPの英訳もきちんとしたものは無いです。調べてみたところ、 Bottom Up/Top Down や Forward/Backward などありますがどれもここで説明した貰う/配るという意味とは異なります。個人的に一番近いのは Take/Give ですが一般的に当てはまる英語は無いです。

練習問題 (貰う/配るDPどちらでも解けます)

Climbing Stairs
難易度: ★★ 重要度: 高
Climbing Stairs - LeetCode
Can you solve this real interview question? Climbing Stairs - You are climbing a staircase. It takes n steps to reach the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?   Example 1: Input: n = 2 Output: 2 Explanation: There are two ways to climb to the top. 1. 1 step + 1 step 2. 2 steps Example 2: Input: n = 3 Output: 3 Explanation: There are three ways to climb to the top. 1. 1 step + 1 step + 1 step 2. 1 step + 2 steps 3. 2 steps + 1 step   Constraints: * 1 <= n <= 45
Climbing Stairs - LeetCode
https://leetcode.com/problems/climbing-stairs/description/
Climbing Stairs - LeetCode
ヒント
貰う、配るDPどちらでも解けます。
iステップ目がi-1とi-2ステップ目から求まる貰うDPで考えることができます。またはiステップ目からi+1とi+2ステップ目へと行けるので、iステップ目の通り数を配ることを考えても解けます。
解答リンク

Min Cost Climbing Stairs
難易度: ★★ 重要度: 高
Min Cost Climbing Stairs - LeetCode
Can you solve this real interview question? Min Cost Climbing Stairs - You are given an integer array cost where cost[i] is the cost of ith step on a staircase. Once you pay the cost, you can either climb one or two steps. You can either start from the step with index 0, or the step with index 1. Return the minimum cost to reach the top of the floor.   Example 1: Input: cost = [10,15,20] Output: 15 Explanation: You will start at index 1. - Pay 15 and climb two steps to reach the top. The total cost is 15. Example 2: Input: cost = [1,100,1,1,1,100,1,1,100,1] Output: 6 Explanation: You will start at index 0. - Pay 1 and climb two steps to reach index 2. - Pay 1 and climb two steps to reach index 4. - Pay 1 and climb two steps to reach index 6. - Pay 1 and climb one step to reach index 7. - Pay 1 and climb two steps to reach index 9. - Pay 1 and climb one step to reach the top. The total cost is 6.   Constraints: * 2 <= cost.length <= 1000 * 0 <= cost[i] <= 999
Min Cost Climbing Stairs - LeetCode
https://leetcode.com/problems/min-cost-climbing-stairs/description/
Min Cost Climbing Stairs - LeetCode
ヒント
貰う、配るどちらでも解けます。iステップ目からi+1とi+2ステップ目へと行けます。後は最小のコストを意識するだけです。
解答リンク

 
動的計画法(導入)動的計画法(基礎)”まで”を状態として扱う, 状態の拡張
def fib(n): # memo[i]: i番目のフィボナッチ数 memo = [0 for _ in range(n + 1)] # 0番目と1番目のフィボナッチ数は1と決まっているので初期化で設定しておく memo[0] = 1 memo[1] = 1 for i in range(2, n + 1): # (1) # i番目のフィボナッチ数はi-1番目のi-2番目のフィボナッチ数の和 # i番目を求めるためにi-1番目とi-2番目の値を貰っている memo[i] = memo[i - 1] + memo[i - 2] return memo[n]
def fib(N): if N < 2: return 1 # 1つ前(prev)と2つ前(pprev)のフィボナッチ数を保持しておく # 最初は1番目の0番目のフィボナッチ数で初期化 prev, pprev = 1, 1 # 現在のフィボナッチ数、最初はなんでも良いのでとりあえず-1で初期化しておく cur = -1 for _ in range(2, N + 1): # 1つ前と2つ前のフィボナッチ数を合計することで現在のフィボナッチ数を求めている cur = prev + pprev # 1つ前と2つ前のフィボナッチ数を更新 prev, pprev = cur, prev return cur
def fib(N): # memo[i]: i番目のフィボナッチ数 memo = [0 for _ in range(N + 1)] # 0番目のフィボナッチ数は1と決まっているので初期化で設定しておく # 1番目のフィボナッチ数はfor文内で0番目と同じ数となるように計算されているので、 # ここでは何も設定しない memo[0] = 1 for i in range(N): # (1) # i番目のフィボナッチ数はi+1番目のi+2番目のフィボナッチ数に寄与される # この様にi番目の値をi+1番目とi+2番目の数に配っている memo[i + 1] += memo[i] if i + 2 < N + 1: memo[i + 2] += memo[i] return memo[N]