今回は paiza の「座標系での規則的な移動」の問題に挑戦!
難しかった(´;ω;`)
問題概要
- 開始地点の (
X,Y) 座標と 移動ステップ数Nが与えられる - 移動パターンは、中心(0,0)から 時計回りの渦巻き
- 東 → 南 → 西 → 北 → 繰り返し
- 座標系は
- 👇 下方向が y 座標の正の向き
- 👉 右方向が x 座標の正の向き
- 時計回りの移動規則に従って
N歩進んだ後の「座標」を出力する
図:
20 21 22 23 24 25
19 6 7 8 9 ↓
18 5 0 1 10
17 4 3 2 11
16 15 14 13 12
入力例:
0 0 3 // X Y N
出力例:
0 1 // x y
✅ OK例:
const rl = require('readline').createInterface({ input: process.stdin });
const lines = [];
rl.on('line', l => lines.push(l));
rl.on('close', () => {
let [sx, sy, N] = lines[0].split(' ').map(Number);
// ESWN の順 → 時計回り
const direct = ['N', 'E', 'S', 'W'];
let x = 0, y = 0; // 座標変化(相対座標)
let length = 1; // 現在の移動マス数
let now = 1; // length の初期値
let d = 1; // 方向インデックス(右回転していく)
let first = true; // その移動マス数の1回目かどうか
const move = dir => {
if (dir === 'N') y--;
else if (dir === 'S') y++;
else if (dir === 'E') x++;
else if (dir === 'W') x--;
};
for (let i = 0; i < N; i++) {
move(direct[d % 4]);
length--;
if (!first && length === 0) {
// 2回目の移動終了 → 次のマス数へ
first = true;
now++;
length = now;
d++;
} else if (length === 0) {
// 1回目の移動終了 → もう一度同じマス数
length = now;
first = false;
d++;
}
}
console.log(sx + x, sy + y);
});
🔍 コードの流れ
- 入力を受け取る
- 開始位置
sx,syと歩数Nを取得する
- 開始位置
- 方角リストを用意
-
['N','E','S','W']
※配列direct[d % 4]を使い、順番に
北 → 東 → 南 → 西 → 北… と繰り返す
-
- 座標変化を表す変数を初期化
- (
x,y) = (0, 0) ← ここに移動量を足していく(相対座標)
- (
- 移動に関する管理用変数のセット
-
length:今の方向で進むマス数(最初は1) -
now:次に設定するlengthの元になる値(初期1) -
d:方向インデックス(右回転を表す) -
first:そのマス数で 1回目の移動 かどうか
-
- ループ開始(
N回移動する)- 現在向く方向 =
direct[d % 4]
その方向に 1マスだけ移動 -
length--(残りマス数を更新)
- 現在向く方向 =
- マス数を使い切ったときに分岐
-
first == true(1回目終了)
→first = falseにして、同じマス数でもう一度
→ 方向を1つ右回転 -
first == false(2回目終了)
→ 次はマス数を1増やしてnow++
→first = trueに戻す
→ 方向を1つ右回転
-
- ループ終了後
- 開始座標に移動量を加算して結果出力
-
sx + x,sy + y
🗒️ まとめ
- 方向制御は配列を使って (
d % 4) で管理
→['N', 'E', 'S', 'W'] - 移動回数の規則性
→ マス数は「同じ値を 2 回ずつ増やしながら進む」 - 管理すべき変数
-
length:現在の残りの移動マス数 -
now:次に設定する移動マス数の基準値 -
d:移動方向を示すインデックス -
first:そのマス数で1回目の移動かを管理
-
- 移動後は 開始位置 + 相対的な移動量 で最終座標を算出
🧸 おまけ:駄作記録
const rl = require('readline').createInterface({ input: process.stdin });
const lines = [];
rl.on('line', input => lines.push(input));
rl.on('close', () => {
const [X, Y, N] = lines[0].split(' ').map(Number);
let x = X;
let y = Y;
let p = 1;
let j = 0;
let k = 0;
let dir = null;
for (let i = 0; i <= N; i++) {
// if (i > 24) console.log(dir + 'direction');
if (dir === 'N') {
y--;
j++;
} else if (dir === 'S') {
y++;
j++;
} else if (dir === 'E') {
x++;
j++;
} else if (dir === 'W') {
x--;
j++;
}
// console.log(p, i, j, 'start')
if (i === p - 1) {
j = 0;
p = (Math.sqrt(p) + 2) ** 2;
// console.log(p)
k++;
// let j = 0;
// console.log(j, 'reset')
dir = 'E';
}
else if (j < Math.sqrt(p) - 1) {
// console.log(i, j, 'ijS')
dir = 'S';
} else if (j < (Math.sqrt(p) - 1) + (Math.sqrt(p) - 1)) {
dir = 'W';
} else if (i < Math.sqrt(p) * (Math.sqrt(p) - 1)) {
// console.log(j, 'one', (Math.sqrt(p) - 1) + (Math.sqrt(p) - 1))
// console.log(Math.sqrt(p) * (Math.sqrt(p) - 1), 'two')
dir = 'N';
} else {
dir = 'E';
}
// console.log(j, i, 'ji')
// console.log (Math.sqrt(p) - k + 'hoge')
// console.log((Math.sqrt(p) - k) + (Math.sqrt(p) - 1) + 'hogek');
// console.log(p, i, j, 'p i j')
}
console.log(x, y);
}) ;
このコード自体は、一番初めに書いたもので、とりあえず答えがあってればいいやって感じで解いた。
(謎の変数 k とか残ってるし(´;ω;`))
コメントアウトは、途中で値を確認するためのもの。
ctrl + / でコメントアウトしたり解除したりして、実行を繰り返して確認しながら解いた。単体テスト的な?