Skip to content

Commit ea9da0d

Browse files
committed
leetcode 18. 4Sum & Minimum selected partial sum problem
1 parent 0b85918 commit ea9da0d

7 files changed

Lines changed: 986 additions & 0 deletions

File tree

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// 以下は、**Node.js (v18.16.1)** + **fsモジュール** を使い、
2+
// 「部分和問題(最小個数)」を解く実装です。
3+
4+
// ## 解法概要
5+
6+
// * **動的計画法 (DP)** を使用
7+
// * `dp[i] = Kを作るために使う数の最小個数`
8+
// 初期値:`dp[0]=0`、それ以外は`Infinity`
9+
// * 最後に`dp[K]`が`Infinity`なら`-1`を出力
10+
// そうでなければ`dp[K]`を出力
11+
12+
// ## 時間計算量:
13+
14+
// * `O(N × K)` (N=1000, K=2000 → 最大約200万回)
15+
16+
// ## メモリ消費:
17+
18+
// * `O(K)`
19+
// (dp配列サイズ:2001個 → 約16KB程度)
20+
21+
// ---
22+
23+
// ### Node.js 解答コード(`fs`で時間計測・メモリ計測)
24+
25+
// ```javascript
26+
const fs = require('fs');
27+
28+
/**
29+
* 部分和問題(最小個数)の解を求める関数
30+
* @param {number} N - 数列の長さ
31+
* @param {number} K - 目標和
32+
* @param {number[]} A - 数列 a_1, a_2, ..., a_N
33+
* @returns {number} - Kを作れる場合は最小個数、作れない場合は-1
34+
*/
35+
function minSubsetSumCount(N, K, A) {
36+
const dp = new Array(K + 1).fill(Infinity);
37+
dp[0] = 0; // 0を作るのに使う個数は0
38+
39+
for (let i = 0; i < N; i++) {
40+
for (let j = K; j >= A[i]; j--) {
41+
if (dp[j - A[i]] !== Infinity) {
42+
dp[j] = Math.min(dp[j], dp[j - A[i]] + 1);
43+
}
44+
}
45+
}
46+
47+
return dp[K] === Infinity ? -1 : dp[K];
48+
}
49+
50+
// 時間・メモリ計測開始
51+
const startTime = process.hrtime.bigint(); // ナノ秒
52+
const startMem = process.memoryUsage().heapUsed; // バイト
53+
54+
// 入力読み込み
55+
const input = fs.readFileSync('/dev/stdin', 'utf8').trim().split('\n');
56+
const [N, K] = input[0].split(' ').map(Number);
57+
const A = input[1].split(' ').map(Number);
58+
59+
// 処理実行
60+
const result = minSubsetSumCount(N, K, A);
61+
console.log(result);
62+
63+
// 時間・メモリ計測終了
64+
const endTime = process.hrtime.bigint();
65+
const endMem = process.memoryUsage().heapUsed;
66+
67+
const elapsedTimeMs = Number(endTime - startTime) / 1e6; // ミリ秒
68+
const memoryUsageMB = (endMem - startMem) / (1024 * 1024); // メガバイト
69+
70+
// デバッグ用(標準出力には出さない)
71+
// fs.writeFileSync('performance_log.txt', `Time: ${elapsedTimeMs.toFixed(3)} ms\nMemory: ${memoryUsageMB.toFixed(3)} MB\n`);
72+
73+
// ## 出力について
74+
75+
// * `result` のみ標準出力します(問題仕様通り)
76+
// * 処理時間・メモリはファイル出力で確認可能(デバッグ時に利用)
77+
78+
// ---
79+
80+
// ## 実行例:
81+
82+
// ### 入力:
83+
84+
// ```
85+
// 3 6
86+
// 1 2 3
87+
// ```
88+
89+
// ### 出力:
90+
91+
// ```
92+
// 3
93+
// ```
94+
95+
// ---
96+
97+
// ## ポイント:
98+
99+
// * **部分和の最小個数問題** → `0-1 Knapsack` のバリエーション
100+
// * 逆ループ(`j=K→A[i]`)により「同じ要素を複数回使わない」仕様を実現
101+
102+
// ---
103+
104+
// ### ご注意:
105+
106+
// もし**複数回使ってよい場合**は、ループ順を`j=0→K`に変える必要があります。
107+
// 今回の問題は「選ぶ数列からいくつか選ぶ」なので**同じ要素は1回のみ使用可能**です。
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# このエラーは、**Python 3.8以前**または、
2+
# **古いオンラインジャッジ環境**で発生しやすいものです。
3+
4+
# 原因:
5+
6+
# ```python
7+
# list[int]
8+
# ```
9+
10+
# という記法は **Python 3.9以降** の正式サポートです。
11+
12+
# ## 【解決策】
13+
14+
# Python 3.8 / 3.7 / 3.6 / CPython 3.11.4(古い互換環境)では
15+
# `typing.List` を使う必要があります。
16+
17+
# ---
18+
19+
# ### **修正版コード**
20+
21+
# ```python
22+
import sys
23+
import time
24+
import tracemalloc
25+
from typing import List
26+
27+
class Solution:
28+
def minSubsetSumCount(self, N: int, K: int, A: List[int]) -> int:
29+
"""
30+
部分和問題の最小個数版を解く関数
31+
32+
Parameters:
33+
N (int): 数列の長さ
34+
K (int): 目標和
35+
A (List[int]): 数列 a_1, a_2, ..., a_N
36+
37+
Returns:
38+
int: Kを作れる場合は最小個数、作れない場合は-1
39+
"""
40+
dp: List[float] = [float('inf')] * (K + 1)
41+
dp[0] = 0 # 0を作るには何も選ばないので0個
42+
43+
for i in range(N):
44+
for j in range(K, A[i]-1, -1): # 逆ループ(同じ要素を複数回使わない)
45+
if dp[j - A[i]] != float('inf'):
46+
dp[j] = min(dp[j], dp[j - A[i]] + 1)
47+
48+
return -1 if dp[K] == float('inf') else int(dp[K])
49+
50+
if __name__ == "__main__":
51+
# 計測開始
52+
start_time = time.perf_counter()
53+
tracemalloc.start()
54+
55+
# 入力読み込み
56+
N_K = sys.stdin.readline().split()
57+
N, K = int(N_K[0]), int(N_K[1])
58+
A = list(map(int, sys.stdin.readline().split()))
59+
60+
# ソリューション実行
61+
sol = Solution()
62+
result: int = sol.minSubsetSumCount(N, K, A)
63+
print(result)
64+
65+
# 計測終了
66+
current, peak = tracemalloc.get_traced_memory()
67+
end_time = time.perf_counter()
68+
tracemalloc.stop()
69+
70+
# デバッグ用(標準出力には出さない)
71+
with open("performance_log.txt", "w") as f:
72+
f.write(f"Time: {(end_time - start_time) * 1000:.3f} ms\n")
73+
f.write(f"Memory: {peak / (1024 * 1024):.3f} MB\n")
74+
# ```
75+
76+
# ---
77+
78+
# ## 【ポイント】
79+
80+
# | 項目 | 対応 |
81+
# | ---------- | ------------------------------------- |
82+
# | 型ヒント互換性エラー | `list[int]` → `List[int]` に修正 |
83+
# | 対応バージョン | Python 3.6~3.11互換 |
84+
# | メモリ&時間計測 | `tracemalloc` + `time.perf_counter()` |
85+
86+
# ---
87+
88+
# ## 【計算量】
89+
90+
# * **時間計算量**:O(N × K)
91+
# * **メモリ消費量**:O(K)(約16KB)
92+
93+
# ---
94+
95+
# ## 【注意】
96+
97+
# オンラインジャッジやLeetCode、AtCoderでは、
98+
# **`typing.List` を使うのが無難**です。

0 commit comments

Comments
 (0)