{ }codelper
switch나머지P0401

악보를 k반음 옮겨 적기

악보에 적힌 음을 모두 k반음씩 옮겨 적으려 한다.
음은 음이름 뒤에 옥타브 숫자를 붙여 C4, F#3, Bb5처럼 적는다.
음이름은 대문자 C, D, E, F, G, A, B 가운데 한 글자이고, 그 바로 뒤에 올림표 #이나 내림표 b(소문자)가 하나 붙을 수 있다.
올림표와 내림표는 일곱 글자 어디에나 붙을 수 있어서 E#, Fb, B#, Cb도 입력에 나온다.
글자마다 정해진 반음 수는 C 0, D 2, E 4, F 5, G 7, A 9, B 11이다.
음의 높이는 (옥타브 숫자) × 12 + (글자의 반음 수)이고, 올림표가 붙으면 여기에 1을 더하고 내림표가 붙으면 1을 뺀다.
그래서 B#4와 C5는 높이가 60으로 같고, Cb4와 B3은 높이가 47로 같다.
음마다 높이에 k를 더한 새 높이를 구해, 그 높이의 음을 다음 규칙으로 적어 출력하라.
새 높이를 12로 나눈 나머지 0, 1, 2, …, 11에 차례로 음이름 C, C#, D, D#, E, F, F#, G, G#, A, A#, B를 쓴다.
그 뒤에 새 높이를 12로 나눈 몫을 옥타브 숫자로 붙인다.
출력에는 내림표를 쓰지 않는다.

입력
첫 줄에 음의 개수 n과 옮길 반음 수 k가 빈칸 하나로 띄워 주어진다.
둘째 줄에 음 n개가 빈칸 하나로 띄워 주어진다.
1 ≤ n ≤ 1000, -12 ≤ k ≤ 12이고, k가 음수이면 높이가 낮아진다.
입력에 적힌 옥타브 숫자는 2 이상 6 이하이다.
따라서 새 높이는 늘 11 이상 96 이하이다.
출력
옮긴 음 n개를 입력된 순서대로 한 줄에 빈칸 하나로 띄워 출력한다.
예시 1 · stdin
5 3
C4 B4 F#3 Bb5 E2
stdout
D#4 D5 A3 C#6 G2
예시 2 · stdin
3 0
B#4 Cb4 Fb3
stdout
C5 B3 E3
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

글자 하나를 수로 바꾸는 일곱 갈래가 이 문제의 switch예요.
C·C++의 switch는 정수와 문자만 받아서 case 'C':처럼 쓰고, 문자열을 넣으면 컴파일 오류예요.
JS·TS는 case "C":로 문자열도 견줄 수 있어요.
Java는 화살표 꼴 case 'C' -> 0;, C#은 switch 식 'C' => 0,, Python은 3.10부터 있는 match/case "C":, 크레이콘은 선택 (letter) { 경우 "C": … 그만; }으로 써요.
함정 하나: break를 빠뜨리기. C·C++·Java(콜론 꼴)·JS·TS의 switch는 break가 없으면 다음 case로 이어 내려가요.
case 'E': s = 4; 뒤의 break를 빼면 E가 F의 값 5를 받아서, 예시 1의 E2가 G2가 아니라 G#2로 나와요.
여기 실린 C 풀이는 갈래마다 break를 쓰고, C++·JS·TS 풀이는 갈래마다 return으로 바로 나가요.
C#은 내용이 있는 case가 다음으로 이어 내려가면 컴파일 오류(CS0163)이고, Java의 화살표 꼴, C#의 switch 식, Python의 match, 크레이콘의 선택은 이어 내려가지 않아요.
함정 둘: 옥타브를 따로 두고 음이름 자리만 계산하기. 글자의 반음 수에 올림표·내림표와 k를 더한 값 s로 NAMES[s % 12]와 옥타브 + s / 12를 구하면, 예시 2의 Cb4에서 s가 -1이 돼요.
C·C++·Java·C#·JS·TS·크레이콘에서 -1 % 12는 -1이라 목록 밖을 가리켜요.
JS에서는 NAMES[-1]이 undefined이고 여기에 수를 더해 NaN이 찍혀요.
Python만은 -1 % 12가 11, -1 // 12가 -1이라 같은 식으로도 B3이 나와요.
여기 실린 풀이는 모두 옥타브 × 12까지 합친 높이 하나로 계산해요.
높이는 늘 11 이상이라 어느 언어에서도 나머지와 몫이 음수가 되지 않아요.
몫은 C·C++·Java·C#에서는 정수끼리의 /, Python은 //, JS·TS는 Math.floor(p / 12), 크레이콘은 수학.몫(p, 12)예요.
함정 셋: 올림표·내림표가 옥타브를 넘기는 것을 잊기. 음이름은 올림표까지 넣어 고르면서 옥타브는 글자와 k만으로 정하면, 예시 2의 B#4가 C5가 아니라 C4로, Cb4가 B3이 아니라 B4로 나와요.

Pythonmain.py · 31 줄
NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]  def semitone(letter):    # 음이름 글자가 C에서 반음 몇 개 위인지 (match는 다음 case로 이어 내려가지 않는다)    match letter:        case "C":            return 0        case "D":            return 2        case "E":            return 4        case "F":            return 5        case "G":            return 7        case "A":            return 9        case _:            return 11  # B  n, k = map(int, input().split())out = []for note in input().split():    p = int(note[-1]) * 12 + semitone(note[0])  # 음 번호: 옥타브와 음이름을 한 수로    if len(note) == 3:        p += 1 if note[1] == "#" else -1    p += k    out.append(NAMES[p % 12] + str(p // 12))print(" ".join(out))
JavaScriptmain.js · 25 줄
const NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; // 음이름 글자가 C에서 반음 몇 개 위인지function semitone(letter) {  switch (letter) {    case "C": return 0;    case "D": return 2;    case "E": return 4;    case "F": return 5;    case "G": return 7;    case "A": return 9;    default: return 11; // B  }} const lines = require("fs").readFileSync(0, "utf8").split("\n");const k = Number(lines[0].trim().split(/\s+/)[1]);const out = lines[1].trim().split(/\s+/).map((note) => {  // 음 번호: 옥타브와 음이름을 한 수로 합친다  let p = Number(note[note.length - 1]) * 12 + semitone(note[0]);  if (note.length === 3) p += note[1] === "#" ? 1 : -1;  p += k;  return NAMES[p % 12] + Math.floor(p / 12);});console.log(out.join(" "));
TypeScriptmain.ts · 27 줄
import { readFileSync } from "node:fs"; const NAMES: string[] = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; // 음이름 글자가 C에서 반음 몇 개 위인지function semitone(letter: string): number {  switch (letter) {    case "C": return 0;    case "D": return 2;    case "E": return 4;    case "F": return 5;    case "G": return 7;    case "A": return 9;    default: return 11; // B  }} const lines: string[] = readFileSync(0, "utf8").split("\n");const k: number = Number(lines[0].trim().split(/\s+/)[1]);const out: string[] = lines[1].trim().split(/\s+/).map((note: string): string => {  // 음 번호: 옥타브와 음이름을 한 수로 합친다  let p: number = Number(note[note.length - 1]) * 12 + semitone(note[0]);  if (note.length === 3) p += note[1] === "#" ? 1 : -1;  p += k;  return NAMES[p % 12] + Math.floor(p / 12);});console.log(out.join(" "));
JavaMain.java · 35 줄
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader; public class Main {    static final String[] NAMES = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};     // 음이름 글자가 C에서 반음 몇 개 위인지 (화살표 switch는 이어 내려가지 않는다)    static int semitone(char letter) {        return switch (letter) {            case 'C' -> 0;            case 'D' -> 2;            case 'E' -> 4;            case 'F' -> 5;            case 'G' -> 7;            case 'A' -> 9;            default -> 11; // B        };    }     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int k = Integer.parseInt(in.readLine().trim().split("\\s+")[1]);        StringBuilder out = new StringBuilder();        for (String note : in.readLine().trim().split("\\s+")) {            // 음 번호: 옥타브와 음이름을 한 수로 합친다            int p = (note.charAt(note.length() - 1) - '0') * 12 + semitone(note.charAt(0));            if (note.length() == 3) p += note.charAt(1) == '#' ? 1 : -1;            p += k;            if (out.length() > 0) out.append(' ');            out.append(NAMES[p % 12]).append(p / 12);        }        System.out.println(out);    }}
C#Main.cs · 34 줄
using System;using System.Collections.Generic; class Program{    static readonly string[] Names = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };     // 음이름 글자가 C에서 반음 몇 개 위인지    static int Semitone(char letter) => letter switch    {        'C' => 0,        'D' => 2,        'E' => 4,        'F' => 5,        'G' => 7,        'A' => 9,        _ => 11, // B    };     static void Main()    {        int k = int.Parse(Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries)[1]);        var result = new List<string>();        foreach (string note in Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries))        {            // 음 번호: 옥타브와 음이름을 한 수로 합친다            int p = (note[note.Length - 1] - '0') * 12 + Semitone(note[0]);            if (note.Length == 3) p += note[1] == '#' ? 1 : -1;            p += k;            result.Add(Names[p % 12] + (p / 12));        }        Console.WriteLine(string.Join(" ", result));    }}
C++main.cpp · 35 줄
#include <iostream>#include <string>using namespace std; const string NAMES[12] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; // 음이름 글자가 C에서 반음 몇 개 위인지int semitone(char letter) {    switch (letter) {        case 'C': return 0;        case 'D': return 2;        case 'E': return 4;        case 'F': return 5;        case 'G': return 7;        case 'A': return 9;        default:  return 11; // B    }} int main() {    int n, k;    cin >> n >> k;    for (int i = 0; i < n; i++) {        string note;        cin >> note;        // 음 번호: 옥타브와 음이름을 한 수로 합친다        int p = (note.back() - '0') * 12 + semitone(note[0]);        if (note.size() == 3) p += note[1] == '#' ? 1 : -1;        p += k;        if (i) cout << ' ';        cout << NAMES[p % 12] << p / 12;    }    cout << '\n';    return 0;}
Cmain.c · 36 줄
#include <stdio.h>#include <string.h> static const char *NAMES[12] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; /* 음이름 글자가 C에서 반음 몇 개 위인지 */static int semitone(char letter) {    int s;    switch (letter) {        case 'C': s = 0; break;        case 'D': s = 2; break;        case 'E': s = 4; break;        case 'F': s = 5; break;        case 'G': s = 7; break;        case 'A': s = 9; break;        default:  s = 11; break; /* B */    }    return s;} int main(void) {    int n, k;    char note[8];    scanf("%d %d", &n, &k);    for (int i = 0; i < n; i++) {        scanf("%7s", note);        size_t len = strlen(note);        /* 음 번호: 옥타브와 음이름을 한 수로 합친다 */        int p = (note[len - 1] - '0') * 12 + semitone(note[0]);        if (len == 3) p += note[1] == '#' ? 1 : -1;        p += k;        printf("%s%s%d", i ? " " : "", NAMES[p % 12], p / 12);    }    printf("\n");    return 0;}
Craykonmain.cray · 53 줄
names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; // 음이름 글자가 C에서 반음 몇 개 위인지반음(letter){    s = 11; // B    선택 (letter)    {        경우 "C":            s = 0;            그만;        경우 "D":            s = 2;            그만;        경우 "E":            s = 4;            그만;        경우 "F":            s = 5;            그만;        경우 "G":            s = 7;            그만;        경우 "A":            s = 9;            그만;    }    돌려주기 s;} first = 콘솔.수들읽기();k = first[1];notes = 콘솔.줄읽기().공백제거().나누기(" ");out = [];각각 (note 에서 notes){    // 음 번호: 옥타브와 음이름을 한 수로 합친다    p = note[note.길이 - 1].수로변환() * 12 + 반음(note[0]);    만약 (note.길이 == 3)    {        만약 (note[1] == "#")        {            p = p + 1;        }        아니면        {            p = p - 1;        }    }    p = p + k;    out.넣기(names[p % 12] + 수학.몫(p, 12));}콘솔.쓰기(out.잇기(" "));