今回は paiza の「「ビームの反射」を解くために:part2」の問題に挑戦!
「ビームの反射」では、壁にビームが当たった時の反射方向を求める必要があるので、これを練習してみる!
問題概要
■ 問題の内容
-
箱の中を進む ビームの進行方向の変化 をシミュレーションする問題。
-
ビームは 最初「右向き」 に進み、
各区画にある「鏡」または「空白」に応じて方向を変えたり、そのまま通過したりする。
与えられたN個の区画を順に処理し、その都度の進行方向を出力する。
■ 入力
N
x_1
x_2
...
x_N
-
N:壁(または区画)の数 - 各
x_iは以下のいずれか- '
_' → 鏡なし(そのまま進む) - '
/' → 右上がり鏡(反射で進行方向が変わる) - '
\' → 右下がり鏡(反射で進行方向が変わる)
- '
■ 出力
- 各区画を通過または反射したときのビームの向きを、毎回出力する。
方向表現:
- 右向き → '
1 0' - 左向き → '
-1 0' - 上向き → '
0 -1' - 下向き → '
0 1'
入力例:
5
\
\
_
\
\
出力例:
0 1
1 0
1 0
0 1
1 0
✅ 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 x = lines.slice(1);
let beam = '1 0'; // 初期方向は右向き
for (let i = 0; i < N; i++) {
const mirror = x[i];
if (mirror === '_') {
console.log(beam);
} else if (mirror === '/') {
// / の反射ルール
if (beam === '1 0') beam = '0 -1';
else if (beam === '-1 0') beam = '0 1';
else if (beam === '0 -1') beam = '1 0';
else if (beam === '0 1') beam = '-1 0';
console.log(beam);
} else if (mirror === '\\') {
// \ の反射ルール
if (beam === '1 0') beam = '0 1';
else if (beam === '-1 0') beam = '0 -1';
else if (beam === '0 -1') beam = '-1 0';
else if (beam === '0 1') beam = '1 0';
console.log(beam);
}
}
});
💡 コードの流れ
① 標準入力の準備
-
readlineを使って入力を1行ずつ読み込む。 - すべての入力を
lines配列に保存。
② 入力終了時の処理 (rl.on('close'))
- 1行目(
lines[0])から壁の個数Nを取得。 - 2行目以降(
lines.slice(1)) に各壁の記号 '_', '/', '\' を格納した配列xを作る。
③ 初期状態の設定
- 最初のビームの向きを右向き "
1 0" として変数beamにセット。
④ 全ての壁を順番に処理(forループ)
- 各壁の種類を
mirrorに取り出す。
⑤ 鏡が '_'(空白)なら
- 鏡が '
_'(空白)なら - 方向は変わらないので、そのまま
beamを出力。
⑥ 鏡が '/' の場合
-
進行方向を「
/」反射のルールに従って変更:- 入射方向 → 反射後方向
- 右(
1 0)→ 上(0 -1) - 左(
-1 0)→ 下(0 1) - 上(
0 -1)→右(1 0) - 下(
0 1)→左(-1 0)
- 右(
- 入射方向 → 反射後方向
-
更新後の
beamを出力。
⑦ 鏡が '\' の場合
-
進行方向を「
\」反射のルールに従って変更:- 入射方向 → 反射後方向
- 右(
1 0)→ 上(0 1) - 左(
-1 0)→ 下(0 -1) - 上(
0 -1)→右(-1 0) - 下(
0 1)→左(1 0)
- 右(
- 入射方向 → 反射後方向
-
更新後の
beamを出力。
⑧ 全ての壁を処理し終えたら終了
✅ ベクトル版
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 x = lines.slice(1);
// 初期のビーム方向(右向き)
let dx = 1;
let dy = 0;
for (let i = 0; i < N; i++) {
const mirror = x[i];
if (mirror === '/') {
// 「/」の場合:dx, dy を入れ替えて両方の符号を反転
[dx, dy] = [-dy, -dx];
} else if (mirror === '\\') {
// 「\」の場合:dx, dy を入れ替える
[dx, dy] = [dy, dx];
}
// "_" の場合はそのまま通過(変更なし)
console.log(`${dx} ${dy}`);
}
});
💡 コードの流れ
① 入力(省略)
② 初期状態の設定
- ビームの初期方向をベクトルで表現:
-
dx = 1, dy = 0(右向き)-
dx,dyの意味:
→ 右 (+1, 0)、左 (-1, 0)、上 (0, -1)、下 (0, +1)
-
-
③ 全ての壁を順に処理(forループ)
- 各壁の種類を
mirrorに代入。
④ 鏡が '/' の場合
- 進行方向を
/に反射させるために、
[dx, dy] = [-dy, -dx];
として dx, dy を入れ替え、両方の符号を反転。
これにより進行方向が正しく反転する。
⑤ 鏡が '\' の場合
- 「
\」は逆向きの斜め鏡。
[dx, dy] = [dy, dx];
として dx と dy を単に入れ替える。
⑥ 鏡が '_' の場合
- 反射しない(方向そのまま)。
⑦ 各ステップの進行方向を出力
- 反射後または通過後の (
dx,dy) を "dx dy" 形式でconsole.log。
⑧ ループ終了でプログラム完了
📝まとめ
- ビームの初期方向は「右向き」
→ (dx, dy) = (1, 0) または "1 0" - 鏡ごとに方向の変換ルールがある
- '
/' :[dx, dy]=[-dy, -dx](両方の軸を入れ替えて符号反転) - '
\' :[dx, dy]=[dy, dx](軸を単純に入れ替える) - '
_' : 変更なし(通過)
- '
- 進行方向は4方向だけ
- 右 (1,0) / 左 (-1,0) / 上 (0,-1) / 下 (0,1)
- 各ステップで反射後の方向を即時出力
→console.log(${dx} ${dy}); - ベクトルで方向を管理するとシンプル
- 条件分岐を減らせて、数学的にスッキリ書ける。
- 反射は「軸の入れ替え+符号の変化」で表現できる。
- テキスト入力の注意
-
\\(バックスラッシュ)は文字化けしやすいので、サンプル入力からコピペ推奨。
-