Skip to content

Latest commit

 

History

History
 
 

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md


問題の概要:

与えられた配列 A = [a₁, a₂, ..., aₙ] から、いくつかの要素を選んで和を K にする。 ただし、同じ位置の要素は1回まで使える(0-1制約)。 和が作れない場合は -1 を出力し、 作れる場合は選んだ要素数が最小となる組み合わせを出力します。

アルゴリズム:動的計画法(0-1ナップサックDP)

DPテーブル定義

  • dp[i][k] 「最初のi個までの要素から選んで、和をkにするときの最小個数」

  • prev[i][k] 「その状態に遷移する直前の和」(復元用)


処理イメージ:図解

例題

A = [1, 3, 2, 2, 1]
K = 4

DPテーブル構築(状態遷移)

初期化

dp[0][0] = 0   (和が0のときは0個選ぶ)
dp[0][k≠0] = ∞ (初期は不可能)

配列イメージ(i=0)

k(和) 0 1 2 3 4
dp[0][k] 0

i=1(a₁=1)

  • 使わない場合: dp[1][k] = dp[0][k]
  • 使う場合: dp[1][k+1] = min(dp[1][k+1], dp[0][k] + 1)
k(和) 0 1 2 3 4
dp[1][k] 0 1

i=2(a₂=3)

  • 使わない場合: dpをそのまま
  • 使う場合:
dp[2][3] = min(dp[2][3], dp[1][0]+1) => 1個(3だけ選択)
dp[2][4] = min(dp[2][4], dp[1][1]+1) => 2個(1+3)
k(和) 0 1 2 3 4
dp[2][k] 0 1 1 2

i=3(a₃=2)

dp[3][2] = min(∞, dp[2][0]+1) => 1個(2)
dp[3][3] = min(1, dp[2][1]+1) => 1個(既にあるのでそのまま)
dp[3][4] = min(2, dp[2][2]+1) => 2個(2+2)
k(和) 0 1 2 3 4
dp[3][k] 0 1 1 1 2

i=4(a₄=2)

  • 同様に更新されるが、dp[3][k]が最適なので更新なし(同じ値が入る)

i=5(a₅=1)

  • 使わない場合:dp[4][k]をコピー
  • 使う場合:
dp[5][1] = min(1, dp[4][0]+1) => 1
dp[5][2] = min(1, dp[4][1]+1) => 1
dp[5][3] = min(1, dp[4][2]+1) => 1
dp[5][4] = min(2, dp[4][3]+1) => 2

最終DPテーブル

k(和) 0 1 2 3 4
dp[5][k] 0 1 1 1 2

復元処理(prev配列を使う)

  • dp[5][4]=2 →「和4を作るため、prev[5][4]=3
  • dp[5][3]=1 →「和3を作るため、prev[5][3]=1

復元した選択肢:

4 → 3 → 1 → 0

選んだ数:3,1 (順番は逆でも良い)


処理フロー全体

初期化 dp[0][0]=0

↓
for i=1..N:
   for k=0..K:
       - 使わない場合 dp[i+1][k]=dp[i][k]
       - 使う場合 dp[i+1][k+A[i]]=min(dp[i+1][k+A[i]], dp[i][k]+1)

↓
復元(prev配列から逆追跡)

↓
出力(個数 + 選んだ要素)

図まとめ(状態遷移)

          i-1
          |
       ┌──┴──┐
   使わない  使う
     ↓        ↓
  dp[i][k]   dp[i][k+A[i]] = min(dp[i][k+A[i]], dp[i-1][k]+1)

計算量

項目
時間計算量 O(N × K)
空間計算量 O(N × K)

メモリ・時間計測

const startTime = process.hrtime.bigint();
...(処理)...
const endTime = process.hrtime.bigint();
console.error(`Time: ${(endTime - startTime) / 1e6} ms`);

const used = process.memoryUsage();
console.error(`Memory: ${Math.round(used.heapUsed / 1024)} KB`);

まとめ

処理 内容
DP配列 和kを作る最小個数を保持
prev配列 選んだ要素を復元するための履歴
復元処理 和Kから逆に辿る