Skip to content

Commit 58c7242

Browse files
committed
atcoder B17 - Frog 1 with Restoration DP
1 parent a4bec55 commit 58c7242

6 files changed

Lines changed: 691 additions & 0 deletions

File tree

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// 以下は、**Go 1.20.6** での「カエルの最小コスト移動経路」問題の実装です。
2+
3+
// * **型を明示**
4+
// * **処理時間:O(N)**、**メモリ使用量:O(N)**
5+
// * 経路復元付き
6+
// * 標準入力は `bufio.Scanner` で高速処理
7+
8+
// ---
9+
10+
// ## ✅ Go 実装(`main.go`)
11+
12+
package main
13+
14+
import (
15+
"bufio"
16+
"fmt"
17+
"os"
18+
"strconv"
19+
)
20+
21+
func abs(a int) int {
22+
if a < 0 {
23+
return -a
24+
}
25+
return a
26+
}
27+
28+
func main() {
29+
reader := bufio.NewScanner(os.Stdin)
30+
reader.Split(bufio.ScanWords)
31+
32+
// 入力ヘルパー関数
33+
readInt := func() int {
34+
reader.Scan()
35+
n, _ := strconv.Atoi(reader.Text())
36+
return n
37+
}
38+
39+
// 入力
40+
N := readInt()
41+
h := make([]int, N)
42+
for i := 0; i < N; i++ {
43+
h[i] = readInt()
44+
}
45+
46+
// DPと経路復元
47+
dp := make([]int, N)
48+
prev := make([]int, N)
49+
50+
const INF int = 1 << 60
51+
for i := range dp {
52+
dp[i] = INF
53+
}
54+
dp[0] = 0
55+
prev[0] = -1
56+
57+
for i := 1; i < N; i++ {
58+
cost1 := dp[i-1] + abs(h[i]-h[i-1])
59+
if cost1 < dp[i] {
60+
dp[i] = cost1
61+
prev[i] = i - 1
62+
}
63+
if i > 1 {
64+
cost2 := dp[i-2] + abs(h[i]-h[i-2])
65+
if cost2 < dp[i] {
66+
dp[i] = cost2
67+
prev[i] = i - 2
68+
}
69+
}
70+
}
71+
72+
// 経路復元
73+
path := []int{}
74+
for cur := N - 1; cur != -1; cur = prev[cur] {
75+
path = append(path, cur+1) // 1-indexed
76+
}
77+
78+
// 逆順出力
79+
for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 {
80+
path[i], path[j] = path[j], path[i]
81+
}
82+
83+
// 出力
84+
fmt.Println(len(path))
85+
for i, v := range path {
86+
if i > 0 {
87+
fmt.Print(" ")
88+
}
89+
fmt.Print(v)
90+
}
91+
fmt.Println()
92+
}
93+
94+
// ## ⏱️ 計算量とメモリ使用量
95+
96+
// | 項目 | 内容 |
97+
// | ------- | --------------------------- |
98+
// | 時間計算量 | O(N) |
99+
// | 空間計算量 | O(N) (`dp`, `prev`, `path`) |
100+
// | 最大入力サイズ | `N <= 10^5` |
101+
// | メモリ目安 | 数MB(標準制限内:1024MiB) |
102+
103+
// ---
104+
105+
// ## 📥 入力例
106+
107+
// ```
108+
// 6
109+
// 30 10 60 10 60 50
110+
// ```
111+
112+
// ## 📤 出力例
113+
114+
// ```
115+
// 4
116+
// 1 3 5 6
117+
// ```
118+
119+
// ---
120+
121+
// ## ✅ 実行方法
122+
123+
// ```bash
124+
// go run main.go < input.txt
125+
// ```
126+
127+
// またはビルド後に:
128+
129+
// ```bash
130+
// go build -o frog main.go
131+
// ./frog < input.txt
132+
// ```
133+
134+
// ---
135+
136+
// ## 🧠 ポイントまとめ
137+
138+
// * `dp[i]`:足場 `i+1` に行くまでの最小コスト
139+
// * `prev[i]`:`i+1` にたどり着く直前の足場
140+
// * 経路は `prev` を逆にたどって出力
141+
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
2+
// * `fs` を使って標準入力を読み込み、
3+
// * 最小コスト経路の復元付きで出力し、
4+
// * 実行時間・メモリ効率に注意した構成です。
5+
6+
// ---
7+
8+
// ### ✅ コード(`index.js`)
9+
10+
// ```javascript
11+
const fs = require("fs");
12+
13+
const input = fs.readFileSync("/dev/stdin", "utf8").trim().split("\n");
14+
const N = parseInt(input[0]);
15+
const h = input[1].split(" ").map(Number);
16+
17+
const dp = new Array(N).fill(Infinity);
18+
const prev = new Array(N).fill(-1);
19+
20+
// 初期化
21+
dp[0] = 0;
22+
23+
for (let i = 1; i < N; i++) {
24+
const cost1 = dp[i - 1] + Math.abs(h[i] - h[i - 1]);
25+
if (cost1 < dp[i]) {
26+
dp[i] = cost1;
27+
prev[i] = i - 1;
28+
}
29+
if (i > 1) {
30+
const cost2 = dp[i - 2] + Math.abs(h[i] - h[i - 2]);
31+
if (cost2 < dp[i]) {
32+
dp[i] = cost2;
33+
prev[i] = i - 2;
34+
}
35+
}
36+
}
37+
38+
// 経路復元
39+
const path = [];
40+
let current = N - 1;
41+
while (current !== -1) {
42+
path.push(current + 1); // 足場番号に変換(1-indexed)
43+
current = prev[current];
44+
}
45+
path.reverse();
46+
47+
// 出力
48+
console.log(path.length);
49+
console.log(path.join(" "));
50+
51+
// ### 💡 解説
52+
53+
// * `dp[i]`: 足場 `i+1` に到達する最小コスト。
54+
// * `prev[i]`: 最小コストで `i` に来たときの直前の足場インデックス。
55+
// * 計算量:`O(N)`
56+
// * メモリ:`O(N)`(配列2本)
57+
58+
// ---
59+
60+
// ### 🧪 入力例1の動作確認
61+
62+
// 入力:
63+
64+
// ```
65+
// 4
66+
// 10 30 40 20
67+
// ```
68+
69+
// 出力(例の1つ):
70+
71+
// ```
72+
// 3
73+
// 1 2 4
74+
// ```
75+
76+
// ---
77+
78+
// ### ⏱️ 実行時間・メモリ効率
79+
80+
// * `N = 100000` でも高速(`O(N)`)
81+
// * 使用メモリ:`~1MB`程度(`dp`, `prev` 各100,000)
82+
83+
// ---
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<!-- 以下は、**PHP 8.2.8** での「カエルの最小コスト移動経路」問題の解法です。
2+
3+
* **型アノテーション対応**(PHP 8.2 準拠)
4+
* **処理時間 O(N)**、**メモリ使用量 O(N)** に抑えた動的計画法(DP)
5+
* **標準入力(STDIN)対応**
6+
* 実行制限:**時間 1秒以内、メモリ 1024MiB以下**
7+
8+
---
9+
10+
## ✅ PHP 実装(`Main.php`)
11+
12+
```php -->
13+
<?php
14+
15+
declare(strict_types=1);
16+
17+
function main(): void {
18+
$input = trim(stream_get_contents(STDIN));
19+
$lines = explode("\n", $input);
20+
21+
$N = intval($lines[0]);
22+
$h = array_map('intval', preg_split('/\s+/', $lines[1]));
23+
24+
/** @var int[] $dp 最小コスト配列 */
25+
$dp = array_fill(0, $N, INF);
26+
/** @var int[] $prev 経路復元用配列 */
27+
$prev = array_fill(0, $N, -1);
28+
29+
$dp[0] = 0;
30+
31+
for ($i = 1; $i < $N; $i++) {
32+
$cost1 = $dp[$i - 1] + abs($h[$i] - $h[$i - 1]);
33+
if ($cost1 < $dp[$i]) {
34+
$dp[$i] = $cost1;
35+
$prev[$i] = $i - 1;
36+
}
37+
38+
if ($i > 1) {
39+
$cost2 = $dp[$i - 2] + abs($h[$i] - $h[$i - 2]);
40+
if ($cost2 < $dp[$i]) {
41+
$dp[$i] = $cost2;
42+
$prev[$i] = $i - 2;
43+
}
44+
}
45+
}
46+
47+
/** @var int[] $path 経路復元 */
48+
$path = [];
49+
$current = $N - 1;
50+
while ($current !== -1) {
51+
$path[] = $current + 1; // 1-indexed
52+
$current = $prev[$current];
53+
}
54+
55+
$path = array_reverse($path);
56+
57+
echo count($path) . "\n";
58+
echo implode(' ', $path) . "\n";
59+
}
60+
61+
main();
62+
// ```
63+
64+
// ---
65+
66+
// ## ⏱️ 計算量・メモリ使用量
67+
68+
// | 項目 | 内容 |
69+
// | ------ | --------------------- |
70+
// | 時間計算量 | O(N) |
71+
// | 空間計算量 | O(N) |
72+
// | メモリ使用量 | 約 1MB(N ≦ 100000 の場合) |
73+
74+
// ---
75+
76+
// ## 📥 入力例
77+
78+
// ```
79+
// 6
80+
// 30 10 60 10 60 50
81+
// ```
82+
83+
// ---
84+
85+
// ## 📤 出力例
86+
87+
// ```
88+
// 4
89+
// 1 3 5 6
90+
// ```
91+
92+
// ---
93+
94+
// ## ✅ 実行方法
95+
96+
// ```bash
97+
// php Main.php < input.txt
98+
// ```
99+
100+
// ---
101+
102+
// ## 💡 補足
103+
104+
// * `dp[i]` は i 番目の足場にたどり着くまでの最小コスト
105+
// * `prev[i]` はどの足場から来たか(経路復元用)
106+
// * 経路は逆順にたどってから `array_reverse()` で正順に戻す
107+
108+
// ---

0 commit comments

Comments
 (0)