Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions Algorithm/DynamicProgramming/atCoder/Knapsack DP/B19/B19.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// 以下は、**Go 1.20.6** における `Knapsack 2(価値軸DP)` の実装です。
// **型の明示・関数の分割・処理時間とメモリの効率を考慮**した構成になっています。

// ## ✅ 構成と特徴

// | 関数名 | 役割 | 引数 / 返却値 |
// | ----------------- | ------------ | ---------------------------- |
// | `parseInput()` | 入力をパース | `() → (int, int, [][2]int)` |
// | `solveKnapsack()` | DPにより最大価値を計算 | `(int, int, [][2]int) → int` |
// | `main()` | 入出力をまとめて呼び出す | `なし` |

// ## ✅ Go 実装(`Go 1.20.6` 準拠、コメント付き)

package main

import (
"bufio"
"fmt"
"math"
"os"
"strconv"
"strings"
)

// parseInput は標準入力から N, W, items を読み取って返します。
// 戻り値: (品物数, 容量W, 品物リスト(重さ, 価値))
func parseInput() (int, int, [][2]int) {
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
header := strings.Fields(scanner.Text())
N, _ := strconv.Atoi(header[0])
W, _ := strconv.Atoi(header[1])

items := make([][2]int, N)
for i := 0; i < N && scanner.Scan(); i++ {
parts := strings.Fields(scanner.Text())
w, _ := strconv.Atoi(parts[0])
v, _ := strconv.Atoi(parts[1])
items[i] = [2]int{w, v}
}
return N, W, items
}

// solveKnapsack は価値軸DPにより、重さ制限W以下で得られる最大価値を計算します。
// 引数: 品物数N、容量W、品物(重さ, 価値)
// 戻り値: 最大の価値
func solveKnapsack(N int, W int, items [][2]int) int {
// 最大の価値合計を取得(DP配列サイズの上限)
maxValue := 0
for _, item := range items {
maxValue += item[1]
}

const INF = math.MaxInt64
dp := make([]int, maxValue+1)
for i := range dp {
dp[i] = INF
}
dp[0] = 0

for _, item := range items {
w, v := item[0], item[1]
for val := maxValue; val >= v; val-- {
if dp[val-v] != INF && dp[val-v]+w < dp[val] {
dp[val] = dp[val-v] + w
}
}
}

// dp[v] <= W となる最大の v を探す
for v := maxValue; v >= 0; v-- {
if dp[v] <= W {
return v
}
}
return 0
}

func main() {
N, W, items := parseInput()
result := solveKnapsack(N, W, items)
fmt.Println(result)
}
// ## ✅ 入力例(例: input.txt)

// ```
// 4 7
// 3 13
// 3 17
// 5 29
// 1 10
// ```

// ### 実行方法:

// ```bash
// go run main.go < input.txt
// ```

// ---

// ## ✅ 計算量・メモリ見積り

// | 項目 | 内容 |
// | ------ | --------------------------------- |
// | 計算量 | `O(N × sum_v)` = `1e7` 程度 |
// | メモリ使用量 | `dp[]` 約 `100000 × 8byte ≒ 800KB` |
// | 実行時間目安 | 数百ms〜1秒以内 |
// | 制限 | 時間:10秒 / メモリ:1024MiB |

// ---

// ## ✅ Go の注意点

// * `math.MaxInt64` を使って `INF` を表現
// * `dp` は 64bit int のスライス(int は通常 64bit)
// * 入力は `bufio.Scanner` + `strings.Fields` で高速処理

// ---

// ## ✅ 拡張可能な内容

// * 選んだ品物の復元
// * メモリ圧縮(必要なら複数スライス併用)
// * 時間軸DP(Wが小さいときに有効)
75 changes: 75 additions & 0 deletions Algorithm/DynamicProgramming/atCoder/Knapsack DP/B19/B19.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 以下は、Node.js 18.16.1 環境で `fs` モジュールを使って解く、\*\*「Knapsack 2(DP with value)」\*\*問題の解法です。

// この問題では `W ≤ 10^9` と非常に大きいので、「重さ」を軸にしたDPではメモリが足りません。代わりに **価値を軸にしたDP(value-based DP)** を使います。

// ---

// ### ✅ 解法の概要(価値DP)

// * `dp[v] = w`: 価値の合計が `v` のときの最小重さ `w`
// * 初期値: `dp[0] = 0`, それ以外は `Infinity`
// * 全ての品物に対して `dp` を更新
// * 最終的に、`dp[v] <= W` となる最大の `v` を答えとする

// ---

// ### ✅ 実装(コメント付き、`fs`使用)

// ```javascript
// Node.js (v18.16.1) 用
const fs = require("fs");

// 入力の読み取り
const input = fs.readFileSync("/dev/stdin", "utf8").trim().split("\n");
const [N, W] = input[0].split(" ").map(Number);
const items = input.slice(1).map(line => {
const [w, v] = line.split(" ").map(Number);
return { w, v };
});

// 最大価値の合計(各価値最大1000 × N)
const maxValue = items.reduce((sum, item) => sum + item.v, 0);

// dp[v] := 価値の合計がvのときの最小重量(初期はInfinity)
const dp = new Array(maxValue + 1).fill(Infinity);
dp[0] = 0; // 価値0のときの重量は0

// 品物を1つずつ見てDPを更新
for (let i = 0; i < N; i++) {
const { w, v } = items[i];

// 逆順に更新することで「1つの品物を1回だけ使う」制約を守る
for (let value = maxValue; value >= v; value--) {
if (dp[value - v] + w <= W) {
dp[value] = Math.min(dp[value], dp[value - v] + w);
}
}
}

// dp[v] <= W を満たす最大の v を探す
let result = 0;
for (let v = 0; v <= maxValue; v++) {
if (dp[v] <= W) {
result = v;
}
}

console.log(result);

/*
返却値: console.log(result)
- 条件を満たす中で最大の価値合計 v を出力します。
- 計算量: O(N * maxValue) ≈ 100 × 100000 = 1e7(制限内)
- メモリ: 約 maxValue 分(数MB程度)
*/

// ### ✅ 実行時間・メモリ見積り

// * **計算量**: `O(N * sum_v)` = `100 × 100000` = `1e7`(高速)
// * **メモリ使用量**: `dp[]` = 約 `100001 × 4bytes` ≒ 約400KB(問題の上限1GBの範囲内)

// ### ✅ 備考

// * 「Wが大きすぎる」=重さでDP配列を作るのは不適
// * 品物の**価値を基準**にDPするのが典型解
// * `O(N×価値合計)` なので、今回のように **価値 ≤ 1000 × N** のとき有効
119 changes: 119 additions & 0 deletions Algorithm/DynamicProgramming/atCoder/Knapsack DP/B19/B19.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<!-- 以下は、**PHP 8.2.8** を使用して、**Knapsack 2(価値軸DP)** を解くための実装です。

## ✅ 特徴

* **関数分割済み**
* **引数と戻り値の型指定**
* **処理時間・メモリ制約(10秒・1024MiB)を考慮**
* `int` 型は PHP 内部で 64bit まで扱える(W = 1e9 に対応可能)

## ✅ 使用関数一覧と説明

| 関数名 | 説明 | パラメータ・返却値 |
| ----------------- | ----------------- | ---------------------------------------------------------------- |
| `parseInput()` | 標準入力からデータを取得して構造化 | 戻り値: `array{int, int, array<array{int, int}>}` |
| `solveKnapsack()` | DP配列で最大の価値を計算 | 引数: `int $N, int $W, array<array{int,int}> $items`<br>返り値: `int` |
| `main()` | 全体統括(入出力) | 戻り値: `void` |

---

## ✅ PHPコード(PHP 8.2.8)

<?php

/**
* 標準入力から値を読み込み、品物情報を構造化して返す
*
* @return array{int, int, array<array{int, int}>} [N, W, items]
*/
function parseInput(): array {
$lines = explode("\n", trim(file_get_contents("php://stdin")));
[$N, $W] = array_map('intval', explode(' ', array_shift($lines)));
$items = [];

foreach ($lines as $line) {
[$w, $v] = array_map('intval', explode(' ', $line));
$items[] = [$w, $v]; // [重さ, 価値]
}

return [$N, $W, $items];
}

/**
* ナップザック問題を価値軸DPで解き、最大の価値を返す
*
* @param int $N 品物数
* @param int $W ナップサック容量
* @param array<array{int, int}> $items 品物の [重さ, 価値] の配列
* @return int 容量W以下で得られる最大の価値
*/
function solveKnapsack(int $N, int $W, array $items): int {
$maxValue = 0;
foreach ($items as [$w, $v]) {
$maxValue += $v;
}

$INF = PHP_INT_MAX;
$dp = array_fill(0, $maxValue + 1, $INF);
$dp[0] = 0;

foreach ($items as [$w, $v]) {
for ($val = $maxValue; $val >= $v; --$val) {
if ($dp[$val - $v] !== $INF && $dp[$val - $v] + $w <= $W) {
$dp[$val] = min($dp[$val], $dp[$val - $v] + $w);
}
}
}

// dp[v] <= W を満たす最大の v を探す
for ($v = $maxValue; $v >= 0; --$v) {
if ($dp[$v] <= $W) {
return $v;
}
}

return 0;
}

/**
* メイン関数:標準入力を読み取り、解を出力
*
* @return void
*/
function main(): void {
[$N, $W, $items] = parseInput();
$result = solveKnapsack($N, $W, $items);
echo $result . PHP_EOL;
}

main();

// ## ✅ 入力例(input.txt)

// ```
// 4 7
// 3 13
// 3 17
// 5 29
// 1 10
// ```

// ### 実行方法

// ```bash
// php main.php < input.txt
// ```

// ## ✅ 計算量・メモリ見積もり

// | 指標 | 値 |
// | ------ | ------------------------------------ |
// | 時間計算量 | `O(N × sum_v)` ≈ 100 × 100000 = 10^7 |
// | メモリ使用量 | 約 400 KB(dp配列:100001 × 8バイト) |
// | 実行時間 | 数百ミリ秒〜1秒以下(十分高速) |
// | メモリ制限 | 1024 MiB 制限内に収まる |

// ## ✅ 補足

// * PHPは `array` 型が柔軟なので、型アノテーションをコメントで補っています。
// * より厳格な型検査をしたい場合は [Psalm](https://psalm.dev/) のような静的解析ツールも有効です。
Loading