第一行给出 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()/标准输入传入(运行中不会等待输入)。
$ 就绪
请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。
其他语言怎么写?查看各语言题解 · 5 种语言题解
这道题只接受有接口或起同样作用的语法的 Python、TypeScript、Java、C# 和 C++。 JavaScript 没有写接口的地方,C 和 Craykon 没有类。 要点是一个类可以实现多个接口,以及运行时怎样问“这个对象实现了那个接口吗”。 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;}