집에 기기 n대가 있고, 기기의 종류는 네 가지다.
- 조명(lamp): 켜고 끌 수 있다. 켜져 있으면 9W, 꺼져 있으면 0W를 쓴다.
- 조절 조명(dimlamp): 켜고 끌 수 있고 단계도 정할 수 있다. 켜져 있으면 단계 × 2W, 꺼져 있으면 0W를 쓴다.
- 선풍기(fan): 단계를 정할 수 있지만 켜고 끌 수는 없다. 늘 단계 × 3W를 쓴다.
- 감지기(sensor): 켜고 끌 수도, 단계를 정할 수도 없다. 늘 1W를 쓴다.
켜고 끌 수 있는 기기는 꺼진 채로 시작하고, 단계를 정할 수 있는 기기는 1단계에서 시작한다.
조절 조명은 꺼져 있을 때도 단계를 바꿀 수 있고, 바꾼 단계는 다시 켰을 때 쓰인다.
명령 q개를 주어진 차례로 처리하며, 명령마다 한 줄씩 출력하라.
- power: 모든 기기가 지금 쓰는 전력의 합을 출력한다.
- allon, alloff: 켜고 끌 수 있는 기기를 모두 켜거나 끈다. 상태가 바뀐 기기의 수를 출력한다.
- alldim v: 단계를 정할 수 있는 기기를 모두 v단계로 맞춘다. 단계가 바뀐 기기의 수를 출력한다.
- on 이름, off 이름: 그 기기를 켜거나 끈다.
- dim 이름 v: 그 기기를 v단계로 맞춘다.
이름을 적은 명령은 그 기기가 그 일을 할 수 없으면 no, 할 수 있지만 이미 그 상태여서 바뀐 것이 없으면 same, 바뀌었으면 ok를 출력한다.
'켜고 끌 수 있다'와 '단계를 정할 수 있다'를 인터페이스 두 개로 만들고, 기기 클래스가 자기가 할 수 있는 것만 구현하게 하자.
기기를 모두 한 목록에 담은 다음, 종류를 묻지 않고 그 인터페이스를 구현했는지만 물어서 처리해 보자.
입력
첫 줄에 n과 q가 빈칸 하나로 나뉘어 주어진다. 다음 n줄에 줄마다 기기의 종류(lamp, dimlamp, fan, sensor 가운데 하나)와 이름이 빈칸 하나로 나뉘어 주어진다. 다음 q줄에 명령이 한 줄에 하나씩 주어지고, 명령 안의 낱말은 빈칸 하나로 나뉜다. 1 ≤ n ≤ 100, 1 ≤ q ≤ 1000, 1 ≤ v ≤ 10. 이름은 영어 소문자·숫자 1~10글자이고 서로 다르다. 명령에 적힌 이름은 모두 있는 기기다
출력
q줄. 명령마다 한 줄씩, 수 하나 또는 ok, same, no 가운데 하나
예시 1 · stdin
4 12
lamp desk
dimlamp bed
fan air
sensor door
power
allon
dim desk 4
alldim 4
on air
power
alloff
dim bed 7
on bed
on bed
allon
power
stdout
4
2
no
2
no
30
2
ok
ok
same
1
36
main.py
오류 목록00F8 ↓ · ⇧F8 ↑
입력(stdin)
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 5개 언어 풀이
이 문제는 인터페이스나 그 구실을 하는 문법이 있는 Python·TypeScript·Java·C#·C++만 받아요. JavaScript는 인터페이스를 적을 곳이 없고, C와 크레이콘에는 클래스가 없어요. 한 클래스가 인터페이스를 여러 개 구현할 수 있다는 점과, 실행할 때 '이 객체가 그 인터페이스를 구현했나'를 묻는 법이 요점이에요. Java는 class DimLamp extends Device implements Switchable, Dimmable이고 d instanceof Switchable s로 묻는 동시에 s로 받아요. C#은 class DimLamp : Device, ISwitchable, IDimmable과 d is ISwitchable s예요. C++은 순수 가상 함수만 가진 구조체를 여러 개 이어받고 dynamic_cast<Switchable*>(d)가 nullptr인지 봐요. Python은 abc.ABC를 여러 개 이어받고 isinstance(d, Switchable)로 물어요. TypeScript 인터페이스는 실행 코드에서 사라져서 d instanceof Switchable이라 쓰면 타입 오류(TS2693)예요. 그래서 "turn" in d로 메서드가 있는지 보고 d is Device & Switchable을 돌려주는 타입 가드 함수를 만들었어요. 함정 하나: 인터페이스가 아니라 구체 클래스로 물으면(dynamic_cast<Lamp*>) 조절 조명이 빠져요. 예시의 allon이 1이 되고 on bed가 no가 돼요. 함정 둘: 묻지 않고 바로 바꾸는 것도 언어마다 달라요. Java의 (Dimmable) d는 실행할 때 검사해서 예시의 dim desk 4에서 ClassCastException으로 멈춰요. TypeScript의 as Device & Dimmable은 실행할 때 아무것도 검사하지 않아서 같은 줄에서 TypeError: d.setLevel is not a function이 나요. 함정 셋: Python에서 인터페이스를 typing.Protocol로 적으면 @runtime_checkable을 붙이지 않는 한 isinstance가 TypeError를 내요(예시의 allon에서 멈춰요). 함정 넷: allon은 켤 수 있는 기기의 수가 아니라 상태가 바뀐 기기의 수예요. 켤 수 있는 기기를 다 세면 예시의 마지막 allon이 1이 아니라 2가 돼요. 그래서 인터페이스의 메서드가 '바뀌었는가'를 돌려주게 했어요. 함정 다섯: 꺼져 있는 조절 조명의 단계를 바꾸지 않으면 예시의 dim bed 7이 same이 되고 마지막 전력이 30이 돼요.
Pythonmain.py · 118 줄
import sysfrom abc import ABC, abstractmethodclass Device(ABC): def __init__(self, name: str): self.name = name @abstractmethod def power(self) -> int: ...class Switchable(ABC): """켜고 끌 수 있다. 상태가 바뀌었으면 True를 돌려준다""" @abstractmethod def turn(self, on: bool) -> bool: ...class Dimmable(ABC): """단계를 정할 수 있다. 단계가 바뀌었으면 True를 돌려준다""" @abstractmethod def set_level(self, v: int) -> bool: ...class Lamp(Device, Switchable): def __init__(self, name: str): super().__init__(name) self.on = False def turn(self, on: bool) -> bool: changed = self.on != on self.on = on return changed def power(self) -> int: return 9 if self.on else 0class DimLamp(Device, Switchable, Dimmable): def __init__(self, name: str): super().__init__(name) self.on = False self.level = 1 def turn(self, on: bool) -> bool: changed = self.on != on self.on = on return changed def set_level(self, v: int) -> bool: # 꺼져 있어도 단계는 바뀐다 changed = self.level != v self.level = v return changed def power(self) -> int: return 2 * self.level if self.on else 0class Fan(Device, Dimmable): def __init__(self, name: str): super().__init__(name) self.level = 1 def set_level(self, v: int) -> bool: changed = self.level != v self.level = v return changed def power(self) -> int: return 3 * self.levelclass Sensor(Device): def power(self) -> int: return 1KINDS = {"lamp": Lamp, "dimlamp": DimLamp, "fan": Fan, "sensor": Sensor}def word(changed: bool) -> str: return "ok" if changed else "same"def main() -> None: lines = sys.stdin.read().split("\n") n, q = map(int, lines[0].split()) devices: list[Device] = [] by_name: dict[str, Device] = {} for i in range(1, n + 1): kind, name = lines[i].split() d = KINDS[kind](name) devices.append(d) by_name[name] = d out = [] for i in range(n + 1, n + 1 + q): p = lines[i].split() cmd = p[0] if cmd == "power": out.append(str(sum(d.power() for d in devices))) elif cmd in ("allon", "alloff"): out.append(str(sum(1 for d in devices if isinstance(d, Switchable) and d.turn(cmd == "allon")))) elif cmd == "alldim": v = int(p[1]) out.append(str(sum(1 for d in devices if isinstance(d, Dimmable) and d.set_level(v)))) elif cmd in ("on", "off"): d = by_name[p[1]] out.append(word(d.turn(cmd == "on")) if isinstance(d, Switchable) else "no") else: # dim 이름 v d = by_name[p[1]] out.append(word(d.set_level(int(p[2]))) if isinstance(d, Dimmable) else "no") print("\n".join(out))main()
TypeScriptmain.ts · 120 줄
import { readFileSync } from "node:fs";// 켜고 끌 수 있다. 상태가 바뀌었으면 true를 돌려준다interface Switchable { turn(on: boolean): boolean;}// 단계를 정할 수 있다. 단계가 바뀌었으면 true를 돌려준다interface Dimmable { setLevel(v: number): boolean;}abstract class Device { constructor(readonly name: string) {} abstract power(): number;}class Lamp extends Device implements Switchable { private on = false; turn(on: boolean): boolean { const changed = this.on !== on; this.on = on; return changed; } power(): number { return this.on ? 9 : 0; }}class DimLamp extends Device implements Switchable, Dimmable { private on = false; private level = 1; turn(on: boolean): boolean { const changed = this.on !== on; this.on = on; return changed; } setLevel(v: number): boolean { // 꺼져 있어도 단계는 바뀐다 const changed = this.level !== v; this.level = v; return changed; } power(): number { return this.on ? 2 * this.level : 0; }}class Fan extends Device implements Dimmable { private level = 1; setLevel(v: number): boolean { const changed = this.level !== v; this.level = v; return changed; } power(): number { return 3 * this.level; }}class Sensor extends Device { power(): number { return 1; }}// 인터페이스는 실행할 때 없으므로, 메서드가 있는지를 보고 타입을 좁혀 주는 함수를 만든다function isSwitchable(d: Device): d is Device & Switchable { return "turn" in d;}function isDimmable(d: Device): d is Device & Dimmable { return "setLevel" in d;}function make(kind: string, name: string): Device { if (kind === "lamp") return new Lamp(name); if (kind === "dimlamp") return new DimLamp(name); if (kind === "fan") return new Fan(name); return new Sensor(name);}const word = (changed: boolean): string => (changed ? "ok" : "same");const lines: string[] = readFileSync(0, "utf8").split("\n");const [n, q] = lines[0].trim().split(/\s+/).map(Number);const devices: Device[] = [];const byName = new Map<string, Device>();for (let i = 1; i <= n; i++) { const [kind, name] = lines[i].trim().split(/\s+/); const d = make(kind, name); devices.push(d); byName.set(name, d);}const out: string[] = [];for (let i = n + 1; i <= n + q; i++) { const p = lines[i].trim().split(/\s+/); const cmd = p[0]; if (cmd === "power") { let total = 0; for (const d of devices) total += d.power(); out.push(String(total)); } else if (cmd === "allon" || cmd === "alloff") { let count = 0; for (const d of devices) if (isSwitchable(d) && d.turn(cmd === "allon")) count++; out.push(String(count)); } else if (cmd === "alldim") { const v = Number(p[1]); let count = 0; for (const d of devices) if (isDimmable(d) && d.setLevel(v)) count++; out.push(String(count)); } else if (cmd === "on" || cmd === "off") { const d = byName.get(p[1])!; out.push(isSwitchable(d) ? word(d.turn(cmd === "on")) : "no"); } else { const d = byName.get(p[1])!; out.push(isDimmable(d) ? word(d.setLevel(Number(p[2]))) : "no"); }}console.log(out.join("\n"));
JavaMain.java · 121 줄
import java.io.*;import java.util.*;// 켜고 끌 수 있다. 상태가 바뀌었으면 true를 돌려준다interface Switchable { boolean turn(boolean on);}// 단계를 정할 수 있다. 단계가 바뀌었으면 true를 돌려준다interface Dimmable { boolean setLevel(int v);}abstract class Device { final String name; Device(String name) { this.name = name; } abstract int power();}class Lamp extends Device implements Switchable { private boolean on = false; Lamp(String name) { super(name); } public boolean turn(boolean on) { boolean changed = this.on != on; this.on = on; return changed; } int power() { return on ? 9 : 0; }}class DimLamp extends Device implements Switchable, Dimmable { private boolean on = false; private int level = 1; DimLamp(String name) { super(name); } public boolean turn(boolean on) { boolean changed = this.on != on; this.on = on; return changed; } public boolean setLevel(int v) { // 꺼져 있어도 단계는 바뀐다 boolean changed = level != v; level = v; return changed; } int power() { return on ? 2 * level : 0; }}class Fan extends Device implements Dimmable { private int level = 1; Fan(String name) { super(name); } public boolean setLevel(int v) { boolean changed = level != v; level = v; return changed; } int power() { return 3 * level; }}class Sensor extends Device { Sensor(String name) { super(name); } int power() { return 1; }}public class Main { static Device make(String kind, String name) { switch (kind) { case "lamp": return new Lamp(name); case "dimlamp": return new DimLamp(name); case "fan": return new Fan(name); default: return new Sensor(name); } } static String word(boolean changed) { return changed ? "ok" : "same"; } public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); int n = Integer.parseInt(st.nextToken()), q = Integer.parseInt(st.nextToken()); List<Device> devices = new ArrayList<>(); Map<String, Device> byName = new HashMap<>(); for (int i = 0; i < n; i++) { st = new StringTokenizer(br.readLine()); String kind = st.nextToken(), name = st.nextToken(); Device d = make(kind, name); devices.add(d); byName.put(name, d); } StringBuilder sb = new StringBuilder(); for (int i = 0; i < q; i++) { st = new StringTokenizer(br.readLine()); String cmd = st.nextToken(); if (cmd.equals("power")) { int total = 0; for (Device d : devices) total += d.power(); sb.append(total); } else if (cmd.equals("allon") || cmd.equals("alloff")) { int count = 0; for (Device d : devices) if (d instanceof Switchable s && s.turn(cmd.equals("allon"))) count++; sb.append(count); } else if (cmd.equals("alldim")) { int v = Integer.parseInt(st.nextToken()); int count = 0; for (Device d : devices) if (d instanceof Dimmable m && m.setLevel(v)) count++; sb.append(count); } else if (cmd.equals("on") || cmd.equals("off")) { Device d = byName.get(st.nextToken()); sb.append(d instanceof Switchable s ? word(s.turn(cmd.equals("on"))) : "no"); } else { Device d = byName.get(st.nextToken()); int v = Integer.parseInt(st.nextToken()); sb.append(d instanceof Dimmable m ? word(m.setLevel(v)) : "no"); } sb.append('\n'); } System.out.print(sb); }}
C#Main.cs · 146 줄
using System;using System.Collections.Generic;using System.Text;// 켜고 끌 수 있다. 상태가 바뀌었으면 true를 돌려준다interface ISwitchable{ bool Turn(bool on);}// 단계를 정할 수 있다. 단계가 바뀌었으면 true를 돌려준다interface IDimmable{ bool SetLevel(int v);}abstract class Device{ public string Name { get; } protected Device(string name) { Name = name; } public abstract int Power();}class Lamp : Device, ISwitchable{ private bool on = false; public Lamp(string name) : base(name) { } public bool Turn(bool on) { bool changed = this.on != on; this.on = on; return changed; } public override int Power() => on ? 9 : 0;}class DimLamp : Device, ISwitchable, IDimmable{ private bool on = false; private int level = 1; public DimLamp(string name) : base(name) { } public bool Turn(bool on) { bool changed = this.on != on; this.on = on; return changed; } public bool SetLevel(int v) // 꺼져 있어도 단계는 바뀐다 { bool changed = level != v; level = v; return changed; } public override int Power() => on ? 2 * level : 0;}class Fan : Device, IDimmable{ private int level = 1; public Fan(string name) : base(name) { } public bool SetLevel(int v) { bool changed = level != v; level = v; return changed; } public override int Power() => 3 * level;}class Sensor : Device{ public Sensor(string name) : base(name) { } public override int Power() => 1;}class Program{ static Device Make(string kind, string name) { switch (kind) { case "lamp": return new Lamp(name); case "dimlamp": return new DimLamp(name); case "fan": return new Fan(name); default: return new Sensor(name); } } static string Word(bool changed) => changed ? "ok" : "same"; static void Main() { var lines = Console.In.ReadToEnd().Split('\n'); var first = lines[0].Split(' ', StringSplitOptions.RemoveEmptyEntries); int n = int.Parse(first[0]), q = int.Parse(first[1]); var devices = new List<Device>(); var byName = new Dictionary<string, Device>(); for (int i = 1; i <= n; i++) { var p = lines[i].Trim().Split(' '); Device d = Make(p[0], p[1]); devices.Add(d); byName[p[1]] = d; } var sb = new StringBuilder(); for (int i = n + 1; i <= n + q; i++) { var p = lines[i].Trim().Split(' '); string cmd = p[0]; if (cmd == "power") { int total = 0; foreach (Device d in devices) total += d.Power(); sb.Append(total); } else if (cmd == "allon" || cmd == "alloff") { int count = 0; foreach (Device d in devices) if (d is ISwitchable s && s.Turn(cmd == "allon")) count++; sb.Append(count); } else if (cmd == "alldim") { int v = int.Parse(p[1]); int count = 0; foreach (Device d in devices) if (d is IDimmable m && m.SetLevel(v)) count++; sb.Append(count); } else if (cmd == "on" || cmd == "off") { Device d = byName[p[1]]; sb.Append(d is ISwitchable s ? Word(s.Turn(cmd == "on")) : "no"); } else { Device d = byName[p[1]]; sb.Append(d is IDimmable m ? Word(m.SetLevel(int.Parse(p[2]))) : "no"); } sb.Append('\n'); } Console.Write(sb); }}
C++main.cpp · 129 줄
#include <iostream>#include <map>#include <memory>#include <string>#include <vector>using namespace std;// 켜고 끌 수 있다. 상태가 바뀌었으면 true를 돌려준다struct Switchable { virtual bool turn(bool on) = 0; virtual ~Switchable() = default;};// 단계를 정할 수 있다. 단계가 바뀌었으면 true를 돌려준다struct Dimmable { virtual bool setLevel(int v) = 0; virtual ~Dimmable() = default;};struct Device { string name; explicit Device(string name) : name(std::move(name)) {} virtual int power() const = 0; virtual ~Device() = default; // 가상 함수가 있어야 dynamic_cast를 쓸 수 있다};class Lamp : public Device, public Switchable { bool on_ = false;public: using Device::Device; bool turn(bool on) override { bool changed = on_ != on; on_ = on; return changed; } int power() const override { return on_ ? 9 : 0; }};class DimLamp : public Device, public Switchable, public Dimmable { bool on_ = false; int level_ = 1;public: using Device::Device; bool turn(bool on) override { bool changed = on_ != on; on_ = on; return changed; } bool setLevel(int v) override { // 꺼져 있어도 단계는 바뀐다 bool changed = level_ != v; level_ = v; return changed; } int power() const override { return on_ ? 2 * level_ : 0; }};class Fan : public Device, public Dimmable { int level_ = 1;public: using Device::Device; bool setLevel(int v) override { bool changed = level_ != v; level_ = v; return changed; } int power() const override { return 3 * level_; }};class Sensor : public Device {public: using Device::Device; int power() const override { return 1; }};unique_ptr<Device> make(const string& kind, const string& name) { if (kind == "lamp") return make_unique<Lamp>(name); if (kind == "dimlamp") return make_unique<DimLamp>(name); if (kind == "fan") return make_unique<Fan>(name); return make_unique<Sensor>(name);}const char* word(bool changed) { return changed ? "ok" : "same"; }int main() { int n, q; cin >> n >> q; vector<unique_ptr<Device>> devices; map<string, Device*> byName; for (int i = 0; i < n; i++) { string kind, name; cin >> kind >> name; devices.push_back(make(kind, name)); byName[name] = devices.back().get(); } for (int i = 0; i < q; i++) { string cmd; cin >> cmd; if (cmd == "power") { int total = 0; for (const auto& d : devices) total += d->power(); cout << total << '\n'; } else if (cmd == "allon" || cmd == "alloff") { int count = 0; for (const auto& d : devices) if (auto* s = dynamic_cast<Switchable*>(d.get()); s && s->turn(cmd == "allon")) count++; cout << count << '\n'; } else if (cmd == "alldim") { int v; cin >> v; int count = 0; for (const auto& d : devices) if (auto* m = dynamic_cast<Dimmable*>(d.get()); m && m->setLevel(v)) count++; cout << count << '\n'; } else if (cmd == "on" || cmd == "off") { string name; cin >> name; auto* s = dynamic_cast<Switchable*>(byName[name]); cout << (s ? word(s->turn(cmd == "on")) : "no") << '\n'; } else { string name; int v; cin >> name >> v; auto* m = dynamic_cast<Dimmable*>(byName[name]); cout << (m ? word(m->setLevel(v)) : "no") << '\n'; } } return 0;}