diff --git a/DataStructures/Trees/Heap/Deadline b/DataStructures/Trees/Heap/Deadline new file mode 100644 index 00000000..3bbe8d2d --- /dev/null +++ b/DataStructures/Trees/Heap/Deadline @@ -0,0 +1,19 @@ +import sys +from typing import List, Tuple + +def main() -> None: + input_data = sys.stdin.read().strip().splitlines() + n: int = int(input_data[0]) + problems: List[Tuple[int, int]] = [] + + for line in input_data[1:]: + parts = line.split() + if len(parts) == 2: + problems.append((int(parts[0]), int(parts[1]))) + else: + raise ValueError("Each line must contain exactly two integers") + + print(solve(n, problems)) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75.go b/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75.go new file mode 100644 index 00000000..b36918eb --- /dev/null +++ b/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75.go @@ -0,0 +1,73 @@ +package main + +import ( + "bufio" + "container/heap" + "fmt" + "os" + "sort" + "strconv" + "strings" +) + +// 問題の構造体 +type Problem struct { + T int // 所要時間 + D int // 締切 +} + +// 最大ヒープの実装 +type MaxHeap []int + +func (h MaxHeap) Len() int { return len(h) } +func (h MaxHeap) Less(i, j int) bool { return h[i] > h[j] } // 逆順で最大ヒープに +func (h MaxHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } +func (h *MaxHeap) Push(x interface{}) { *h = append(*h, x.(int)) } +func (h *MaxHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + +func solve(N int, problems []Problem) int { + // 締切 D で昇順ソート + sort.Slice(problems, func(i, j int) bool { + return problems[i].D < problems[j].D + }) + + totalTime := 0 + h := &MaxHeap{} + heap.Init(h) + + for _, p := range problems { + totalTime += p.T + heap.Push(h, p.T) + + if totalTime > p.D { + removed := heap.Pop(h).(int) + totalTime -= removed + } + } + + return h.Len() +} + +func main() { + scanner := bufio.NewScanner(os.Stdin) + scanner.Scan() + N, _ := strconv.Atoi(scanner.Text()) + + var problems []Problem + for i := 0; i < N; i++ { + scanner.Scan() + parts := strings.Fields(scanner.Text()) + T, _ := strconv.Atoi(parts[0]) + D, _ := strconv.Atoi(parts[1]) + problems = append(problems, Problem{T, D}) + } + + result := solve(N, problems) + fmt.Println(result) +} diff --git a/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75.php b/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75.php new file mode 100644 index 00000000..d61a420f --- /dev/null +++ b/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75.php @@ -0,0 +1,64 @@ +T = $T; + $this->D = $D; + } +} + +/** + * メインの問題解決ロジック + * + * @param int $N + * @param array $problems + * @return int + */ +function solve(int $N, array $problems): int { + // 締切Dの昇順でソート + usort($problems, function (Problem $a, Problem $b): int { + return $a->D <=> $b->D; + }); + + /** @var SplPriorityQueue $heap 最大ヒープとして使用 */ + $heap = new SplPriorityQueue(); + $totalTime = 0; + + foreach ($problems as $p) { + $totalTime += $p->T; + $heap->insert($p->T, $p->T); // priority = T なので最大ヒープ動作 + + if ($totalTime > $p->D) { + $removed = $heap->extract(); + $totalTime -= $removed; + } + } + + return $heap->count(); +} + +/** + * 標準入力からの処理 + */ +function main(): void { + $lines = explode("\n", trim(file_get_contents("php://stdin"))); + $N = intval(array_shift($lines)); + $problems = []; + + foreach ($lines as $line) { + [$T, $D] = array_map('intval', explode(" ", $line)); + $problems[] = new Problem($T, $D); + } + + echo solve($N, $problems) . PHP_EOL; +} + +main(); \ No newline at end of file diff --git a/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75.py b/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75.py new file mode 100644 index 00000000..b158a483 --- /dev/null +++ b/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75.py @@ -0,0 +1,37 @@ +import sys +import heapq +from typing import List, Tuple + +def solve(n: int, problems: List[Tuple[int, int]]) -> int: + # 締切Dで昇順ソート + problems.sort(key=lambda x: x[1]) + + total_time: int = 0 + max_heap: List[int] = [] # Pythonはmin-heapなので、-Tを使って最大ヒープにする + + for t, d in problems: + total_time += t + heapq.heappush(max_heap, -t) # 最大ヒープとして扱うため -t + + if total_time > d: + removed: int = -heapq.heappop(max_heap) # 最大値(時間のかかる問題)を除く + total_time -= removed + + return len(max_heap) + +def main() -> None: + input_data = sys.stdin.read().strip().splitlines() + n: int = int(input_data[0]) + problems: List[Tuple[int, int]] = [] + + for line in input_data[1:]: + parts = line.split() + if len(parts) == 2: + problems.append((int(parts[0]), int(parts[1]))) + else: + raise ValueError("Each line must contain exactly two integers") + + print(solve(n, problems)) + +if __name__ == "__main__": + main() diff --git a/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75.ts b/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75.ts new file mode 100644 index 00000000..864c9d51 --- /dev/null +++ b/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75.ts @@ -0,0 +1,88 @@ +import * as fs from 'fs'; + +// 最大ヒープ(Max Heap)クラスの定義 +class MaxHeap { + private data: number[] = []; + + push(val: number): void { + this.data.push(val); + this.bubbleUp(this.data.length - 1); + } + + pop(): number | null { + if (this.data.length === 0) return null; + const max = this.data[0]; + const last = this.data.pop()!; + if (this.data.length > 0) { + this.data[0] = last; + this.bubbleDown(0); + } + return max; + } + + size(): number { + return this.data.length; + } + + private bubbleUp(index: number): void { + while (index > 0) { + const parent = Math.floor((index - 1) / 2); + if (this.data[parent] >= this.data[index]) break; + [this.data[parent], this.data[index]] = [this.data[index], this.data[parent]]; + index = parent; + } + } + + private bubbleDown(index: number): void { + const length = this.data.length; + while (true) { + let left = 2 * index + 1; + let right = 2 * index + 2; + let largest = index; + + if (left < length && this.data[left] > this.data[largest]) { + largest = left; + } + if (right < length && this.data[right] > this.data[largest]) { + largest = right; + } + if (largest === index) break; + + [this.data[largest], this.data[index]] = [this.data[index], this.data[largest]]; + index = largest; + } + } +} + +// メイン処理関数 +function main(input: string): void { + const lines = input.trim().split('\n'); + const N = parseInt(lines[0]); + const problems: { T: number; D: number }[] = []; + + for (let i = 1; i <= N; i++) { + const [T, D] = lines[i].split(' ').map(Number); + problems.push({ T, D }); + } + + // 締切D順にソート + problems.sort((a, b) => a.D - b.D); + + const heap = new MaxHeap(); + let totalTime = 0; + + for (const { T, D } of problems) { + totalTime += T; + heap.push(T); + + if (totalTime > D) { + const removed = heap.pop()!; + totalTime -= removed; + } + } + + console.log(heap.size()); +} + +// 標準入力の読み込み +main(fs.readFileSync(0, 'utf8')); diff --git a/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75_1.js b/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75_1.js new file mode 100644 index 00000000..def44c7e --- /dev/null +++ b/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75_1.js @@ -0,0 +1,35 @@ +const fs = require("fs"); + +function main(input) { + const lines = input.trim().split("\n"); + const N = parseInt(lines[0]); + const problems = lines.slice(1).map(line => { + const [T, D] = line.split(" ").map(Number); + return { T, D }; + }); + + // 締切順にソート + problems.sort((a, b) => a.D - b.D); + + // 優先度付きキュー(最大ヒープ)として時間を格納する + const timeHeap = []; + let currentTime = 0; + + for (const { T, D } of problems) { + currentTime += T; + timeHeap.push(T); + // 最大ヒープを保つため降順にソート + timeHeap.sort((a, b) => b - a); + + // 締切を超えたら、最も時間のかかる問題を削除 + if (currentTime > D) { + const longest = timeHeap.shift(); // 最大値を削除 + currentTime -= longest; + } + } + + console.log(timeHeap.length); +} + +// 標準入力から +main(fs.readFileSync(0, "utf8")); diff --git a/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75_2.js b/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75_2.js new file mode 100644 index 00000000..d373df59 --- /dev/null +++ b/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/A75_2.js @@ -0,0 +1,87 @@ +const fs = require('fs'); + +// 最大ヒープクラス +class MaxHeap { + constructor() { + this.data = []; + } + + push(val) { + this.data.push(val); + this._bubbleUp(this.data.length - 1); + } + + pop() { + if (this.size() === 0) return null; + const max = this.data[0]; + const last = this.data.pop(); + if (this.size() > 0) { + this.data[0] = last; + this._bubbleDown(0); + } + return max; + } + + size() { + return this.data.length; + } + + _bubbleUp(index) { + while (index > 0) { + const parent = Math.floor((index - 1) / 2); + if (this.data[parent] >= this.data[index]) break; + [this.data[parent], this.data[index]] = [this.data[index], this.data[parent]]; + index = parent; + } + } + + _bubbleDown(index) { + const length = this.data.length; + while (true) { + let left = 2 * index + 1; + let right = 2 * index + 2; + let largest = index; + + if (left < length && this.data[left] > this.data[largest]) { + largest = left; + } + if (right < length && this.data[right] > this.data[largest]) { + largest = right; + } + if (largest === index) break; + + [this.data[largest], this.data[index]] = [this.data[index], this.data[largest]]; + index = largest; + } + } +} + +function main(input) { + const lines = input.trim().split('\n'); + const N = parseInt(lines[0]); + const problems = lines.slice(1).map((line) => { + const [T, D] = line.split(' ').map(Number); + return { T, D }; + }); + + // 締切でソート + problems.sort((a, b) => a.D - b.D); + + let totalTime = 0; + const maxHeap = new MaxHeap(); + + for (const { T, D } of problems) { + totalTime += T; + maxHeap.push(T); + + if (totalTime > D) { + const removed = maxHeap.pop(); // 一番時間のかかる問題を除外 + totalTime -= removed; + } + } + + console.log(maxHeap.size()); +} + +// 標準入力からの読み込み +main(fs.readFileSync(0, 'utf8')); diff --git a/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/README.md b/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/README.md new file mode 100644 index 00000000..901877a7 --- /dev/null +++ b/DataStructures/Trees/Heap/Deadline Scheduling/atcoder/A75/README.md @@ -0,0 +1,197 @@ +**締切付き作業スケジューリング**を、\*\*貪欲法 + 最大ヒープ(MaxHeap)\*\*で解いていきます。以下にステップごとの処理と、**図を交えた具体的な解説** + +--- + +## 🔰 問題概要(再掲) + +* 問題数 `N` 個 +* 各問題 `i` にかかる時間 `T[i]` と締切 `D[i]` +* 各問題は**連続 `T[i]` 分の時間**が必要で、**試験開始から `D[i]` 分以内に終える必要がある** +* **できるだけ多くの問題を解く**のが目的 + +--- + +## 📘 入力例 + +``` +4 +20 70 +30 50 +30 100 +20 60 +``` + +各問題を `(T, D)` で表すと: + +| 問題番号 | T | D | +| ---- | -- | --- | +| 1 | 20 | 70 | +| 2 | 30 | 50 | +| 3 | 30 | 100 | +| 4 | 20 | 60 | + +--- + +## 🧩 ステップ①:締切でソート + +```ts +problems.sort((a, b) => a.D - b.D); +``` + +* 締切が早い問題から順に処理していくことで、**締切に間に合わないリスクを最小化**します。 + +### 🖼 図:ソート後の順序 + +``` +[ (30, 50), (20, 60), (20, 70), (30, 100) ] + ↑ ↑ ↑ ↑ + 問題2 問題4 問題1 問題3 +``` + +--- + +## 🧩 ステップ②:時間を積み上げて問題を解く(ヒープ管理) + +### 🧠 方針 + +* `totalTime += T[i]` で積み上げる +* もし `totalTime > D[i]` になったら、「一番時間がかかる問題」を除去(←ヒープで管理) + +--- + +## 🧮 各ステップの図解付き解説 + +--- + +### ✅ ステップ0(初期状態) + +```ts +totalTime = 0 +heap = [] +``` + +--- + +### ✅ ステップ1:問題2(30, 50) + +```ts +totalTime += 30 → 30 <= 50 OK +heap.push(30) +``` + +🖼 ヒープ(最大ヒープ) + +``` +[30] +``` + +--- + +### ✅ ステップ2:問題4(20, 60) + +```ts +totalTime += 20 → 50 <= 60 OK +heap.push(20) +``` + +🖼 ヒープ + +``` +[30, 20] +``` + +--- + +### ✅ ステップ3:問題1(20, 70) + +```ts +totalTime += 20 → 70 <= 70 OK +heap.push(20) +``` + +🖼 ヒープ + +``` +[30, 20, 20] +``` + +--- + +### ✅ ステップ4:問題3(30, 100) + +```ts +totalTime += 30 → 100 <= 100 OK +heap.push(30) +``` + +🖼 ヒープ + +``` +[30, 30, 20, 20] +``` + +--- + +## ✅ 結果:heapのサイズが答え(=解けた問題数) + +```ts +heap.size() → 4 +``` + +--- + +## 🧠 締切オーバーが起きるケースのイメージ図(参考) + +例:あるステップで `totalTime = 90`、次の問題が `(T = 30, D = 100)` のとき: + +``` +totalTime += 30 → 120 > 100 → NG! +⇒ 一番大きい T を削除(ヒープから) +``` + +🖼 Before: + +``` +Heap: [40, 30, 20] → totalTime = 90 ++ New T = 30 +⇒ totalTime = 120 > D = 100 +``` + +🖼 After: + +``` +Heap.pop() → Remove 40 +totalTime -= 40 → totalTime = 80(OK) +``` + +--- + +## 💡 なぜ「一番大きなTを削除」? + +* できるだけ**多くの問題を解く**のが目標。 +* 時間超過したら、**時間のかかる問題を外す方が他の問題をより多く残せる**から。 + +--- + +## ✅ 最終まとめ図 + +``` +処理順: 問題2 → 問題4 → 問題1 → 問題3 + (30,50) (20,60) (20,70) (30,100) + +累積時間: 30 → 50 → 70 → 100(すべて締切内) + +最終ヒープ内容: [30, 30, 20, 20] + +解けた問題数 = ヒープのサイズ = 4 ✅ +``` + +--- +| [提出日時](https://atcoder.jp/contests/tessoku-book/submissions/me?desc=true&orderBy=created) | 問題 | ユーザ | 言語 | [得点](https://atcoder.jp/contests/tessoku-book/submissions/me?desc=true&orderBy=score) | [コード長](https://atcoder.jp/contests/tessoku-book/submissions/me?orderBy=source_length) | 結果 | [実行時間](https://atcoder.jp/contests/tessoku-book/submissions/me?orderBy=time_consumption) | [メモリ](https://atcoder.jp/contests/tessoku-book/submissions/me?orderBy=memory_consumption) | | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 2025-06-30 14:44:25 | [A75 - Examination](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_bw) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [PHP (php 8.2.8)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5016) | 1000 | 1461 Byte | **AC** | 16 ms | 21660 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67196693) | +| 2025-06-30 14:42:42 | [A75 - Examination](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_bw) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [Go (go 1.20.6)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5002) | 1000 | 1469 Byte | **AC** | 1 ms | 1628 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67196652) | +| 2025-06-30 14:41:30 | [A75 - Examination](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_bw) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [Python (CPython 3.11.4)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5055) | 1000 | 1129 Byte | **AC** | 20 ms | 10700 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67196627) | +| 2025-06-30 14:19:04 | [A75 - Examination](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_bw) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [TypeScript 5.1 (Node.js 18.16.1)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5058) | 1000 | 2404 Byte | **AC** | 47 ms | 42956 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67196216) | +| 2025-06-30 14:14:11 | [A75 - Examination](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_bw) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [JavaScript (Node.js 18.16.1)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5009) | 1000 | 2277 Byte | **AC** | 41 ms | 42816 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67196132) | +| 2025-06-30 14:12:40 | [A75 - Examination](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_bw) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [JavaScript (Node.js 18.16.1)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5009) | 1000 | 1015 Byte | **AC** | 42 ms | 42812 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67196100) | \ No newline at end of file