カメが一匹、(0, 0) で北を向いて立っている。 北に一歩なら y が 1 増え、東なら x が 1 増え、南なら y が 1 減り、西なら x が 1 減る。 一行に命令の文字が空白なしで与えられる。 F は向いている方へ一歩、R は右に 90 度回る、L は左に 90 度回る、B はその場で後ろを向く(180 度)。 命令をすべて終えたあと、カメの x、y と向いている方角の頭文字(N・E・S・W のどれか)を空白一つで区切って出力せよ。 方角は北・東・南・西の四つの値を時計回りの順に持つ列挙型(enum)にして解いてみよう。
入力
命令の長さは 1 以上 100,000 以下、文字は F・R・L・B のどれか
出力
一行:x y 方角
例 1 · stdin
FFRFFLFBFF
stdout
2 1 S
例 2 · stdin
LF
stdout
-1 0 W
main.py
エラー一覧00F8 ↓ · ⇧F8 ↑
入力(stdin)
実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。
$ 準備完了
コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。
ほかの言語では?言語別の解答を見る · 6 言語の解答
この問題は列挙型で解ける Python・TypeScript・Java・C#・C・C++ だけを受け付けます。 Python は標準モジュール enum の class Dir(Enum) で作ります。 JavaScript には列挙型の構文がなく(Object.freeze({...}) でまねるだけ)外れ、Craykon にも列挙型はありません。 TypeScript の enum Dir { North, East, South, West } は値が 0〜3 の数値列挙型なので、(d + 1) % 4 のように数で計算してから as Dir で戻し、逆引き表 Dir[d] が名前 "North" をくれます。 enum は型だけの構文ではなく、実行時に使うオブジェクトを作り出します。 列挙型の中身は言語ごとに違います。 C の enum Dir { NORTH, EAST, SOUTH, WEST } は名前の付いた int 定数 0〜3 にすぎず、実行中は名前を知らないので "NESW"[d] のように名札を自分で持ちます。 C++ の enum class は Dir::North と書き、int に勝手に変わらないので static_cast<int>(d) が要ります。 Java の enum はクラスで values()、ordinal()、name() を持ち、C# は (Dir)(((int)d + 1) % 4) のように行き来して d.ToString() が名前をくれます。 Python は Dir(値)、d.value、d.name です。 落とし穴三つ:① 左回りを (d - 1) % 4 と書くと、北(0)から C・C++・Java・C#・TypeScript では -1 になります(余りは割られる数の符号に従います)。 C# は (Dir)(-1) もエラーなしで作り "-1" を出し、TypeScript も -1 as Dir を止めないので Dir[-1] が undefined になります。 (d + 3) % 4 と書きます(Python は -1 % 4 == 3 なので大丈夫)。 ② C・C++(と Java の古い case: switch)で break を忘れると次の case に落ちて、一歩で何度も動きます。 C# はこれをコンパイルエラーで防ぎ、Java の矢印 switch case NORTH -> y++; は落ちません。 ③ Python の Enum のメンバは数と等しくないので d == 0 はいつも False です(IntEnum なら True)。 d == Dir.NORTH か d.value で比べます。
Pythonmain.py · 23 行
from enum import Enumclass Dir(Enum): # 시계 방향 차례 NORTH = 0 EAST = 1 SOUTH = 2 WEST = 3STEP = {Dir.NORTH: (0, 1), Dir.EAST: (1, 0), Dir.SOUTH: (0, -1), Dir.WEST: (-1, 0)}TURN = {"R": 1, "B": 2, "L": 3} # 왼쪽 = 오른쪽으로 세 번x = y = 0d = Dir.NORTHfor c in input().strip(): if c == "F": dx, dy = STEP[d] x += dx y += dy else: d = Dir((d.value + TURN[c]) % 4) # 수로 바꿔 계산하고 다시 열거형으로print(x, y, d.name[0])
TypeScriptmain.ts · 34 行
import { readFileSync } from "node:fs";enum Dir { // 시계 방향 차례: North = 0, East = 1, South = 2, West = 3 North, East, South, West,}// 숫자 enum은 수로 계산할 수 있다. 결과를 다시 Dir로 돌려놓는다function turn(d: Dir, k: number): Dir { return ((d + k) % 4) as Dir; // 왼쪽은 -1 대신 +3}const s = readFileSync(0, "utf8").trim();let x = 0;let y = 0;let d = Dir.North;for (const c of s) { switch (c) { case "R": d = turn(d, 1); break; case "B": d = turn(d, 2); break; case "L": d = turn(d, 3); break; default: switch (d) { case Dir.North: y++; break; case Dir.East: x++; break; case Dir.South: y--; break; case Dir.West: x--; break; } }}// 역방향 표 Dir[d]가 이름("South")을 준다console.log(`${x} ${y} ${Dir[d].charAt(0)}`);
JavaMain.java · 33 行
import java.io.BufferedReader;import java.io.InputStreamReader;public class Main { enum Dir { NORTH, EAST, SOUTH, WEST } // 시계 방향 차례 static Dir turn(Dir d, int k) { Dir[] all = Dir.values(); return all[(d.ordinal() + k) % 4]; } public static void main(String[] args) throws Exception { String s = new BufferedReader(new InputStreamReader(System.in)).readLine().trim(); int x = 0, y = 0; Dir d = Dir.NORTH; for (char c : s.toCharArray()) { switch (c) { case 'R' -> d = turn(d, 1); case 'B' -> d = turn(d, 2); case 'L' -> d = turn(d, 3); default -> { switch (d) { // 화살표 switch는 아래로 흘러내리지 않는다 case NORTH -> y++; case EAST -> x++; case SOUTH -> y--; case WEST -> x--; } } } } System.out.println(x + " " + y + " " + d.name().charAt(0)); }}
C#Main.cs · 34 行
using System;enum Dir { North, East, South, West } // 시계 방향 차례class Program{ static Dir Turn(Dir d, int k) => (Dir)(((int)d + k) % 4); // 열거형 ↔ int는 명시적으로 바꾼다 static void Main() { string s = Console.ReadLine().Trim(); int x = 0, y = 0; Dir d = Dir.North; foreach (char c in s) { switch (c) { case 'R': d = Turn(d, 1); break; case 'B': d = Turn(d, 2); break; case 'L': d = Turn(d, 3); break; default: switch (d) { case Dir.North: y++; break; case Dir.East: x++; break; case Dir.South: y--; break; case Dir.West: x--; break; } break; } } Console.WriteLine($"{x} {y} {d.ToString()[0]}"); }}
C++main.cpp · 34 行
#include <iostream>#include <string>enum class Dir { North, East, South, West }; // 범위 있는 열거형: Dir::North로 쓰고 int로 저절로 바뀌지 않는다Dir turn(Dir d, int k) { return static_cast<Dir>((static_cast<int>(d) + k) % 4);}char name(Dir d) { return "NESW"[static_cast<int>(d)]; // C++도 열거형의 이름을 실행 중에 모른다}int main() { std::string s; std::cin >> s; int x = 0, y = 0; Dir d = Dir::North; for (char c : s) { switch (c) { case 'R': d = turn(d, 1); break; case 'B': d = turn(d, 2); break; case 'L': d = turn(d, 3); break; default: switch (d) { case Dir::North: y++; break; case Dir::East: x++; break; case Dir::South: y--; break; case Dir::West: x--; break; } } } std::cout << x << " " << y << " " << name(d) << "\n";}
Cmain.c · 32 行
#include <stdio.h>enum Dir { NORTH, EAST, SOUTH, WEST }; /* 이름 붙은 int 상수 0, 1, 2, 3 */static const char NAME[] = "NESW"; /* C 열거형은 실행 중에 이름을 모른다: 직접 둔다 */enum Dir turn(enum Dir d, int k) { return (enum Dir)((d + k) % 4);}int main(void) { static char s[100002]; scanf("%100001s", s); int x = 0, y = 0; enum Dir d = NORTH; for (int i = 0; s[i]; i++) { switch (s[i]) { case 'R': d = turn(d, 1); break; case 'B': d = turn(d, 2); break; case 'L': d = turn(d, 3); break; default: switch (d) { case NORTH: y++; break; /* break가 없으면 다음 case로 흘러내린다 */ case EAST: x++; break; case SOUTH: y--; break; case WEST: x--; break; } } } printf("%d %d %c\n", x, y, NAME[d]); return 0;}