Skip to content

Commit 770f0d3

Browse files
committed
atcoder B38 - Heights of Grass 貪欲+逆方向補正
1 parent 024c651 commit 770f0d3

6 files changed

Lines changed: 738 additions & 0 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// 以下は、問題に対する **Go 1.20.6** での実装です。
2+
// **処理時間(1秒以内)とメモリ使用量(1024MiB以内)** を意識し、効率的な **O(N)** アルゴリズムを採用しています。
3+
4+
// ---
5+
6+
// ## ✅ 解法概要(再掲)
7+
8+
// * 各草の初期高さを 1 に設定。
9+
// * `'A'` に対して **左→右** に昇順制約を補正。
10+
// * `'B'` に対して **右→左** に降順制約を補正。
11+
// * 最終的な配列の合計が最小となる。
12+
13+
// ---
14+
15+
// ## ✅ Go実装(型明示・関数化・コメント付き)
16+
17+
// ```go
18+
package main
19+
20+
import (
21+
"bufio"
22+
"fmt"
23+
"os"
24+
"strconv"
25+
// "strings"
26+
)
27+
28+
/*
29+
computeMinimumTotalHeight は草の高さの最小合計を計算する関数です。
30+
31+
Parameters:
32+
- n int: 草の本数 (1 <= n <= 3000)
33+
- s string: 'A' または 'B' からなる長さ n-1 の文字列
34+
35+
Returns:
36+
- int: 高さの合計の最小値
37+
*/
38+
func computeMinimumTotalHeight(n int, s string) int {
39+
height := make([]int, n)
40+
for i := range height {
41+
height[i] = 1 // 初期高さは全て1
42+
}
43+
44+
// 左から 'A' 条件に従い補正
45+
for i := 0; i < n-1; i++ {
46+
if s[i] == 'A' && height[i] >= height[i+1] {
47+
height[i+1] = height[i] + 1
48+
}
49+
}
50+
51+
// 右から 'B' 条件に従い補正
52+
for i := n - 2; i >= 0; i-- {
53+
if s[i] == 'B' && height[i] <= height[i+1] {
54+
height[i] = height[i+1] + 1
55+
}
56+
}
57+
58+
// 合計計算
59+
total := 0
60+
for _, h := range height {
61+
total += h
62+
}
63+
return total
64+
}
65+
66+
func main() {
67+
scanner := bufio.NewScanner(os.Stdin)
68+
scanner.Scan()
69+
n, _ := strconv.Atoi(scanner.Text())
70+
71+
scanner.Scan()
72+
s := scanner.Text()
73+
74+
result := computeMinimumTotalHeight(n, s)
75+
fmt.Println(result)
76+
}
77+
// ```
78+
79+
// ---
80+
81+
// ## ✅ 入力例と実行方法
82+
83+
// ### 実行例:
84+
85+
// ```bash
86+
// echo -e "7\nAABBBA" | go run main.go
87+
// # 出力: 15
88+
// ```
89+
90+
// または、ファイルで保存している場合:
91+
92+
// ```bash
93+
// go run solve.go < input.txt
94+
// ```
95+
96+
// ---
97+
98+
// ## ✅ 処理性能
99+
100+
// * **時間計算量:** `O(N)`(`n=3000` でも高速)
101+
// * **空間計算量:** `O(N)`(高さスライスのみ)
102+
103+
// Goの速度と組み合わせて、問題の制限内(1 sec, 1024 MiB)で安定して動作します。
104+
105+
// ---
106+
107+
// ## ✅ 型と設計上の注意点
108+
109+
// * 関数 `computeMinimumTotalHeight` は引数・返り値の型を明示。
110+
// * 入力の読み取りには `bufio.Scanner` を使用して **高速入力対応**。
111+
// * 入力エラー処理は簡略化(本番環境では `err != nil` チェック推奨)。
112+
113+
// ---
114+
115+
// ご希望があれば以下も対応可能です:
116+
117+
// * 入力が全てAや全てBの特殊ケースでの検証
118+
// * 単体テスト用の関数群追加
119+
// * スライスの途中状態のデバッグ出力表示
120+
121+
// どうぞ気軽にお知らせください。
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// 以下は、問題に対して **Node.js (v18.16.1)** + `fs` モジュールを用いて、**実行時間・メモリ制限に配慮**した最適な解法です。
2+
3+
// ### ✅ 解法の概要
4+
5+
// * 高さは **1以上の整数**。
6+
// * 隣接の草の大小関係が `'A'` / `'B'` で指定されている。
7+
// * 高さの **合計が最小**になるように、矛盾しないように割り当てる。
8+
// * **グラフとしてのトポロジカルソート + DP的な更新**でも解けるが、より簡単な方法としては **貪欲+逆走補正**が有効。
9+
10+
// ---
11+
12+
// ### ✅ アルゴリズム(貪欲+逆方向補正)
13+
14+
// 1. 左から順に、Aが続いていれば1,2,3...と上昇。
15+
// 2. Bが出てきたら、いったん1とし、必要があれば**後ろ向きに補正**。
16+
17+
// * 連続したBに対して、**山のような形**になるようにする。
18+
19+
// ---
20+
21+
// ### ✅ JavaScript実装(Node.js 18.16.1 + fs)
22+
23+
// ```javascript
24+
// @ts-check
25+
'use strict';
26+
27+
const fs = require('fs');
28+
29+
/**
30+
* 草の高さの合計として考えられる最小値を計算する
31+
* @param {number} N - 草の本数
32+
* @param {string} S - 長さN-1の文字列。'A'または'B'が含まれる。
33+
* @returns {number} - 最小の合計値
34+
*/
35+
function minTotalGrassHeight(N, S) {
36+
const height = Array(N).fill(1);
37+
38+
// 1. 左からAに従って昇順にしていく
39+
for (let i = 0; i < N - 1; i++) {
40+
if (S[i] === 'A' && height[i] >= height[i + 1]) {
41+
height[i + 1] = height[i] + 1;
42+
}
43+
}
44+
45+
// 2. 右からBに従って降順に調整
46+
for (let i = N - 2; i >= 0; i--) {
47+
if (S[i] === 'B' && height[i] <= height[i + 1]) {
48+
height[i] = height[i + 1] + 1;
49+
}
50+
}
51+
52+
// 合計を返す
53+
return height.reduce((sum, h) => sum + h, 0);
54+
}
55+
56+
// 標準入力から読み込み
57+
function main() {
58+
const input = fs.readFileSync(0, 'utf-8').trim().split('\n');
59+
const N = parseInt(input[0], 10);
60+
const S = input[1];
61+
62+
const result = minTotalGrassHeight(N, S);
63+
console.log(result);
64+
}
65+
66+
main();
67+
// ```
68+
69+
// ---
70+
71+
// ### ✅ 実行時間・メモリ使用
72+
73+
// * **時間計算量:** `O(N)`(2回のループだけ)
74+
// * **空間計算量:** `O(N)`(高さ配列のみ)
75+
76+
// ---
77+
78+
// ### ✅ 入力例と出力例
79+
80+
// ```bash
81+
// $ echo -e "7\nAABBBA" | node solve.js
82+
// 15
83+
// ```
84+
85+
// ---
86+
87+
// ### ✅ 解法のポイント補足
88+
89+
// * `A`に従って**前方向に貪欲**。
90+
// * `B`に従って**後方向に調整**。
91+
// * 各草の高さが「最小になるようにかつ条件を満たす」ための定番テクニックです。
92+
93+
// ---
94+
95+
// ご希望があれば、図解や他言語での実装、コードゴルフなども対応可能です。
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<!-- 以下は、問題に対する **PHP 8.2.8** を用いた解答です。
2+
処理時間(1秒)、メモリ制限(1024MiB)を考慮し、**O(N) 時間・O(N) 空間**の効率的なアルゴリズムを採用しています。
3+
4+
---
5+
6+
## ✅ 解法概要(再掲)
7+
8+
* 各草の高さを最小の整数(1)で初期化。
9+
* `'A'` に対して前から昇順補正、`'B'` に対して後ろから降順補正。
10+
* 最終的な高さの合計が最小になる。
11+
12+
---
13+
14+
## ✅ PHP 8.2.8 実装
15+
16+
```php -->
17+
<?php
18+
19+
/**
20+
* 草の高さの合計の最小値を計算する。
21+
*
22+
* @param int $n 草の本数(1 <= $n <= 3000)
23+
* @param string $s 草の比較条件(長さ $n - 1、'A' または 'B' からなる)
24+
* @return int 草の高さの合計として考えられる最小値
25+
*/
26+
function computeMinimumTotalHeight(int $n, string $s): int {
27+
// 各草の初期高さは1
28+
$height = array_fill(0, $n, 1);
29+
30+
// 左から 'A' 条件に従って昇順に補正
31+
for ($i = 0; $i < $n - 1; $i++) {
32+
if ($s[$i] === 'A' && $height[$i] >= $height[$i + 1]) {
33+
$height[$i + 1] = $height[$i] + 1;
34+
}
35+
}
36+
37+
// 右から 'B' 条件に従って降順に補正
38+
for ($i = $n - 2; $i >= 0; $i--) {
39+
if ($s[$i] === 'B' && $height[$i] <= $height[$i + 1]) {
40+
$height[$i] = $height[$i + 1] + 1;
41+
}
42+
}
43+
44+
// 合計値を返す
45+
return array_sum($height);
46+
}
47+
48+
/**
49+
* エントリポイント:標準入力を読み込み、結果を出力する。
50+
*/
51+
function main(): void {
52+
$lines = explode("\n", trim(file_get_contents("php://stdin")));
53+
$n = (int)$lines[0];
54+
$s = trim($lines[1]);
55+
56+
$result = computeMinimumTotalHeight($n, $s);
57+
echo $result . PHP_EOL;
58+
}
59+
60+
main();
61+
// ```
62+
63+
// ---
64+
65+
// ## ✅ 入力例と実行例
66+
67+
// ### 入力:
68+
69+
// ```
70+
// 7
71+
// AABBBA
72+
// ```
73+
74+
// ### 実行:
75+
76+
// ```bash
77+
// php solve.php
78+
// ```
79+
80+
// 入力を手動で入れるか、パイプで渡す:
81+
82+
// ```bash
83+
// echo -e "7\nAABBBA" | php solve.php
84+
// # 出力: 15
85+
// ```
86+
87+
// ---
88+
89+
// ## ✅ 処理性能
90+
91+
// * **時間計算量:** `O(N)`(最大3000回のループ)
92+
// * **空間計算量:** `O(N)`(高さ配列)
93+
94+
// PHP 8.2.8 の実行速度でも制限内(1秒・1024MiB)で十分処理可能です。
95+
96+
// ---
97+
98+
// ## ✅ コメントと型について
99+
100+
// * PHP 8.2.8 では関数の引数・戻り値に **厳格な型指定**が可能(`int`, `string`, `void`など)。
101+
// * `declare(strict_types=1);` を使うとさらに安全な型チェックも可能(必要であれば追加できます)。
102+
103+
// ---
104+
105+
// ご希望があれば:
106+
107+
// * `unittest`によるテスト例
108+
// * 入力が全てBや全てAなどの特殊パターン例
109+
// * 処理中のデバッグ表示付き実装
110+
111+
// なども対応可能です。お気軽にどうぞ。

0 commit comments

Comments
 (0)