今回 paiza の「値の計算」の問題に挑戦!
問題概要
問題の内容
- 複数種類の抵抗(名前と抵抗値が与えられる)がある。
- それらの抵抗を直列または並列でつないで、1つの回路を作る。
- 回路の全体抵抗を求め、小数点以下を切り捨てて出力する。
抵抗の接続の計算ルール
- 直列
A + B
- 並列
1 / (1 / A + 1 / B)
入力仕様
- 1行目
整数N
(抵抗の種類の数)
-
2行目~(N+1)行目
抵抗名s_i
(英大文字1文字)と抵抗値w_i
(整数)が与えられる。- 例:
A 100
- 例:
-
(N+2)行目
整数M
(接続パターンの数)
- (N+3)行目
抵抗の接続方法を表す文字列t_1 t_2 ... t_M
- 空白で区切られた部分は 直列
- 空白なしで並んだ文字列は 並列
出力仕様
- 回路全体の抵抗値を 小数点以下切り捨て で整数出力する。
入力例:
3
A 100
B 200
C 300
2
AB C
出力例:
366
✅ OK例:
const rl = require('readline').createInterface({ input: process.stdin });
const lines = [];
rl.on('line', input => lines.push(input));
rl.on('close', () => {
const N = Number(lines[0]);
// 抵抗値マップを作成
const sw = new Map();
for (let i = 1; i <= N; i++) {
const [s, w] = lines[i].split(' ');
sw.set(s, Number(w));
}
const M = Number(lines[N + 1]);
const t = lines[N + 2].split(' '); // 空白ごとに直列分割
// --- 並列の抵抗を計算する関数 ---
const parallel = (arr) => {
let denom = 0;
for (const x of arr) {
denom += 1 / sw.get(x);
}
return 1 / denom;
};
// 直列の合計
let total = 0;
for (const part of t) {
if (part.length === 1) {
total += sw.get(part); // 抵抗一個だけ → そのまま
} else {
total += parallel(part.split('')); // 並列
}
}
console.log(Math.floor(total));
});
🔍 コードの流れ
入力を受け取る準備
-
readline
で標準入力を読み込む -
lines
配列に入力を保存
抵抗の種類と値をマップ化
- 1行目 →
N
(抵抗の種類の数) - 次の N 行 →
s w
を読み取り、Map
に「抵抗名 → 抵抗値」として保存
回路の構成を取得
- N+1 行目 →
M
(接続情報の数) - N+2 行目 → 抵抗の接続方法(文字列)を取得し、空白で分割。
空白区切り → 直列 のまとまりとして処理する
並列計算の関数を定義
- 与えられた抵抗名の配列を受け取り
-
1 / (1/a + 1/b + …)
を計算して並列抵抗値を返す
直列合計を計算
- 空白で分割された各部分を順番に処理
- 1文字だけなら → そのまま抵抗値
- 複数文字なら → 並列として計算して加算
結果を出力
- 最終的な合計抵抗値を
Math.floor
で小数点切り捨て -
console.log
で出力
🗒️ まとめ
- 入力処理
- 辞書(
Map
)を使って「抵抗名 → 抵抗値」を素早く参照できるようにする。
- 辞書(
- 回路表現の分解
- 空白で区切れば直列のまとまりになる。
- 文字をまとめて見れば並列のまとまりになる。
- 直列と並列の違い
- 直列 → 単純に和をとる。
- 並列 → 逆数を足し合わせて逆数を取る。
- 実装の工夫
- 並列計算は関数に切り出すと分かりやすい。
-
for
やreduce
を使えば簡潔に書ける。
- ポイント
- 並列は必ず「逆数の和の逆数」になる。
- 計算の最後に
Math.floor
で切り捨てるのを忘れない。