{ }codelper
クラスメソッドP0157

社員の種類ごとに違う給料

会社の社員は三種類で、一か月の記録から給料を計算する。
どの社員も名前とともに、今月働いた時間の合計と販売金額の合計を記録しておく。
基本社員(staff)の給料は基本給である。
時給社員(hourly)の給料は、160 時間までは一時間ごとに時給、160 時間を超えた分は一時間ごとに時給の 2 倍である。
販売社員(sales)の給料は基本給に手当を足したもので、手当は販売金額の合計に割合 p を掛けて 100 で割った値の小数部分を切り捨てた整数である。
働いた時間と販売金額はどの種類の社員にも記録されうるが、給料の計算には上に書いたものだけを使う。
一行目に社員数 n、続く n 行に社員が「staff 名前 基本給」「hourly 名前 時給」「sales 名前 基本給 p」のどれかで与えられる。
次の行に記録数 q、続く q 行に記録が「work 名前 時間」または「sell 名前 金額」で与えられる。
社員ごとに「名前 給料」を入力の順に一行ずつ出力し、最後の行に「total 給料の合計」を出力せよ。
基本社員を基底クラス Employee にし、時給社員と販売社員をそのサブクラスにして給料メソッド pay をオーバーライドしよう。
社員を Employee のリスト一つに入れ、種類を問わずに pay を呼んで計算してみよう。

入力
1 ≤ n ≤ 100、1 ≤ q ≤ 2,000。
名前は英小文字・数字 1 ~ 10 文字で互いに異なる。
0 ≤ 基本給 ≤ 1,000,000、1 ≤ 時給 ≤ 10,000、0 ≤ p ≤ 30、記録の時間は 1 以上 12 以下、金額は 1 以上 10,000 以下の整数。
記録の名前はすべて存在する社員である
出力
n+1 行:社員ごとに「名前 給料」、最後の行に「total 合計」
例 1 · stdin
3
staff ann 3000
hourly bob 10
sales cy 1000 5
5
work bob 12
sell cy 150
work ann 8
sell cy 99
sell ann 500
stdout
ann 3000
bob 120
cy 1012
total 4132
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 6 言語の解答

この問題はクラスの継承がある Python・JavaScript・TypeScript・Java・C#・C++ だけを受け付けます(C にはクラスがなく、Craykon はまだクラスを作れません)。
サブクラスを作る構文は Python class Hourly(Employee)、JS・TS・Java class Hourly extends Employee、C# class Hourly : Employee、C++ class Hourly : public Employee で、販売社員は基底クラスの計算を super().pay()(Python)・super.pay()(JS・TS・Java)・base.Pay()(C#)・Employee::pay()(C++)で呼んで手当だけを足します。
大きく違うのは、オーバーライドがいつ自動で起きるかです。
Python・JS・TS・Java は同じ名前のメソッドを書き直せば常にオーバーライドになります(TypeScript は override を付けると基底にそのメソッドがないとき型エラーになり、noImplicitOverride を有効にすれば override の付け忘れも見つかります)。
C# は基底に virtual、サブクラスに override の両方が必要で、C++ も基底に virtual がなければ Employee* で呼んだときに本当のクラスの pay が呼ばれません。
落とし穴は五つ:① C++ で virtual を忘れてもコンパイルは通りますが、line() の中の pay() が常に Employee::pay になり、時給社員が 0 円になります。
② C++ で std::vector<Employee> のように値で入れると、サブクラスのオブジェクトが切り落とされ(スライシング)基底部分だけが残ります。
std::vector<std::unique_ptr<Employee>> に入れます。
③ C# で override を忘れると警告(CS0114)が出るだけで新しいメソッドとして隠され、Employee として呼ぶと基底の Pay が呼ばれます。
④ Java は @Override がないと、Pay() のように綴りを間違えたメソッドが新しいメソッドとして黙って生まれます。
@Override を付ければコンパイルエラーで見つかります。
⑤ JS・TS の / は小数まで割るので、手当が 12.45 のようになります。
Math.floor で切り捨てます(Python は //、Java・C#・C++ の int の割り算は 0 以上なら自動で切り捨てられます)。
おまけに、Python で sales = [] を __init__ ではなくクラス本体に書くと、全社員が一つのリストを共有します。

Pythonmain.py · 70 行
import sys  class Employee:    """기본 직원: 기본급만 받는다. 일한 시간과 판매 기록은 모든 직원이 가진다."""     def __init__(self, name, base):        self.name = name        self.base = base        self.hours = 0        self.sales = []  # 판매 금액 기록(인스턴스마다 따로 만든다)     def work(self, h):        self.hours += h     def sell(self, amount):        self.sales.append(amount)     def pay(self):        return self.base     def line(self):        return f"{self.name} {self.pay()}"  # 어느 클래스의 pay가 불릴지는 실행 중에 정해진다  class Hourly(Employee):    def __init__(self, name, rate):        super().__init__(name, 0)        self.rate = rate     def pay(self):        over = max(0, self.hours - 160)        return self.rate * (self.hours - over) + 2 * self.rate * over  class Sales(Employee):    def __init__(self, name, base, pct):        super().__init__(name, base)        self.pct = pct     def pay(self):        return super().pay() + sum(self.sales) * self.pct // 100  lines = sys.stdin.read().split("\n")n = int(lines[0])staff = []by_name = {}for i in range(1, n + 1):    p = lines[i].split()    if p[0] == "staff":        e = Employee(p[1], int(p[2]))    elif p[0] == "hourly":        e = Hourly(p[1], int(p[2]))    else:        e = Sales(p[1], int(p[2]), int(p[3]))    staff.append(e)    by_name[e.name] = e q = int(lines[n + 1])for i in range(n + 2, n + 2 + q):    cmd, name, x = lines[i].split()    if cmd == "work":        by_name[name].work(int(x))    else:        by_name[name].sell(int(x)) for e in staff:    print(e.line())print("total", sum(e.pay() for e in staff))
JavaScriptmain.js · 68 行
const lines = require("fs").readFileSync(0, "utf8").split("\n"); // 기본 직원: 기본급만 받는다. 일한 시간과 판매 합계는 모든 직원이 가진다class Employee {  constructor(name, base) {    this.name = name;    this.base = base;    this.hours = 0;    this.sold = 0;  }  work(h) {    this.hours += h;  }  sell(amount) {    this.sold += amount;  }  pay() {    return this.base;  }  line() {    return `${this.name} ${this.pay()}`; // 객체의 진짜 클래스의 pay가 불린다  }} class Hourly extends Employee {  constructor(name, rate) {    super(name, 0);    this.rate = rate;  }  pay() {    const over = Math.max(0, this.hours - 160);    return this.rate * (this.hours - over) + 2 * this.rate * over;  }} class Sales extends Employee {  constructor(name, base, pct) {    super(name, base);    this.pct = pct;  }  pay() {    return super.pay() + Math.floor((this.sold * this.pct) / 100); // JS의 /는 소수까지 나눈다  }} const n = Number(lines[0]);const staff = [];const byName = new Map();for (let i = 1; i <= n; i++) {  const p = lines[i].trim().split(" ");  let e;  if (p[0] === "staff") e = new Employee(p[1], Number(p[2]));  else if (p[0] === "hourly") e = new Hourly(p[1], Number(p[2]));  else e = new Sales(p[1], Number(p[2]), Number(p[3]));  staff.push(e);  byName.set(e.name, e);} const q = Number(lines[n + 1]);for (let i = n + 2; i < n + 2 + q; i++) {  const [cmd, name, x] = lines[i].trim().split(" ");  if (cmd === "work") byName.get(name).work(Number(x));  else byName.get(name).sell(Number(x));} const out = staff.map((e) => e.line());out.push(`total ${staff.reduce((s, e) => s + e.pay(), 0)}`);console.log(out.join("\n"));
TypeScriptmain.ts · 72 行
import { readFileSync } from "node:fs"; // 기본 직원: 기본급만 받는다. 일한 시간과 판매 합계는 모든 직원이 가진다class Employee {  protected hours = 0;  protected sold = 0;   constructor(readonly name: string, protected readonly base: number) {}   work(h: number): void {    this.hours += h;  }   sell(amount: number): void {    this.sold += amount;  }   pay(): number {    return this.base;  }   line(): string {    return `${this.name} ${this.pay()}`; // 객체의 진짜 클래스의 pay가 불린다  }} class Hourly extends Employee {  constructor(name: string, private readonly rate: number) {    super(name, 0);  }   override pay(): number {    const over = Math.max(0, this.hours - 160);    return this.rate * (this.hours - over) + 2 * this.rate * over;  }} class Sales extends Employee {  constructor(name: string, base: number, private readonly pct: number) {    super(name, base);  }   override pay(): number {    return super.pay() + Math.floor((this.sold * this.pct) / 100); // number의 /는 소수까지 나눈다  }} const lines: string[] = readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);const staff: Employee[] = [];const byName = new Map<string, Employee>();for (let i = 1; i <= n; i++) {  const p: string[] = lines[i].trim().split(" ");  let e: Employee;  if (p[0] === "staff") e = new Employee(p[1], Number(p[2]));  else if (p[0] === "hourly") e = new Hourly(p[1], Number(p[2]));  else e = new Sales(p[1], Number(p[2]), Number(p[3]));  staff.push(e);  byName.set(e.name, e);} const q = Number(lines[n + 1]);for (let i = n + 2; i < n + 2 + q; i++) {  const [cmd, name, x] = lines[i].trim().split(" ");  const e = byName.get(name)!;  if (cmd === "work") e.work(Number(x));  else e.sell(Number(x));} const out: string[] = staff.map((e) => e.line());out.push(`total ${staff.reduce((s, e) => s + e.pay(), 0)}`);console.log(out.join("\n"));
JavaMain.java · 72 行
import java.io.*;import java.util.*; public class Main {    // 기본 직원: 기본급만 받는다. 일한 시간과 판매 합계는 모든 직원이 가진다    static class Employee {        final String name;        final int base;        int hours = 0, sold = 0;         Employee(String name, int base) { this.name = name; this.base = base; }         void work(int h) { hours += h; }        void sell(int amount) { sold += amount; }        int pay() { return base; }         String line() { return name + " " + pay(); } // 객체의 진짜 클래스의 pay가 불린다    }     static class Hourly extends Employee {        final int rate;         Hourly(String name, int rate) { super(name, 0); this.rate = rate; }         @Override        int pay() {            int over = Math.max(0, hours - 160);            return rate * (hours - over) + 2 * rate * over;        }    }     static class Sales extends Employee {        final int pct;         Sales(String name, int base, int pct) { super(name, base); this.pct = pct; }         @Override        int pay() { return super.pay() + sold * pct / 100; }    }     public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(br.readLine().trim());        List<Employee> staff = new ArrayList<>();        Map<String, Employee> byName = new HashMap<>();        for (int i = 0; i < n; i++) {            String[] p = br.readLine().trim().split(" ");            Employee e = switch (p[0]) {                case "staff" -> new Employee(p[1], Integer.parseInt(p[2]));                case "hourly" -> new Hourly(p[1], Integer.parseInt(p[2]));                default -> new Sales(p[1], Integer.parseInt(p[2]), Integer.parseInt(p[3]));            };            staff.add(e);            byName.put(e.name, e);        }        int q = Integer.parseInt(br.readLine().trim());        for (int i = 0; i < q; i++) {            String[] p = br.readLine().trim().split(" ");            Employee e = byName.get(p[1]);            if (p[0].equals("work")) e.work(Integer.parseInt(p[2]));            else e.sell(Integer.parseInt(p[2]));        }        StringBuilder sb = new StringBuilder();        int total = 0;        for (Employee e : staff) {            sb.append(e.line()).append('\n');            total += e.pay();        }        sb.append("total ").append(total).append('\n');        System.out.print(sb);    }}
C#Main.cs · 78 行
using System;using System.Collections.Generic;using System.Linq;using System.Text; // 기본 직원: 기본급만 받는다. 일한 시간과 판매 합계는 모든 직원이 가진다class Employee{    public string Name { get; }    protected int Base { get; }    protected int Hours { get; private set; }    protected int Sold { get; private set; }     public Employee(string name, int baseSalary) { Name = name; Base = baseSalary; }     public void Work(int h) => Hours += h;    public void Sell(int amount) => Sold += amount;     // virtual이어야 하위 클래스가 override할 수 있다    public virtual int Pay() => Base;     public string Line() => $"{Name} {Pay()}"; // 객체의 진짜 클래스의 Pay가 불린다} class Hourly : Employee{    private readonly int rate;     public Hourly(string name, int rate) : base(name, 0) { this.rate = rate; }     public override int Pay()    {        int over = Math.Max(0, Hours - 160);        return rate * (Hours - over) + 2 * rate * over;    }} class Sales : Employee{    private readonly int pct;     public Sales(string name, int baseSalary, int pct) : base(name, baseSalary) { this.pct = pct; }     public override int Pay() => base.Pay() + Sold * pct / 100;} class Program{    static void Main()    {        int n = int.Parse(Console.ReadLine()!.Trim());        var staff = new List<Employee>();        var byName = new Dictionary<string, Employee>();        for (int i = 0; i < n; i++)        {            var p = Console.ReadLine()!.Trim().Split(' ');            Employee e = p[0] switch            {                "staff" => new Employee(p[1], int.Parse(p[2])),                "hourly" => new Hourly(p[1], int.Parse(p[2])),                _ => new Sales(p[1], int.Parse(p[2]), int.Parse(p[3])),            };            staff.Add(e);            byName[e.Name] = e;        }        int q = int.Parse(Console.ReadLine()!.Trim());        for (int i = 0; i < q; i++)        {            var p = Console.ReadLine()!.Trim().Split(' ');            if (p[0] == "work") byName[p[1]].Work(int.Parse(p[2]));            else byName[p[1]].Sell(int.Parse(p[2]));        }        var sb = new StringBuilder();        foreach (var e in staff) sb.Append(e.Line()).Append('\n');        sb.Append("total ").Append(staff.Sum(e => e.Pay())).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 87 行
#include <algorithm>#include <iostream>#include <memory>#include <string>#include <unordered_map>#include <vector> // 기본 직원: 기본급만 받는다. 일한 시간과 판매 합계는 모든 직원이 가진다class Employee {public:    Employee(std::string name, int base) : name_(std::move(name)), base_(base) {}    virtual ~Employee() = default;  // 기반 클래스 포인터로 지울 때 하위 소멸자도 불리게     const std::string &name() const { return name_; }    void work(int h) { hours_ += h; }    void sell(int amount) { sold_ += amount; }     // virtual이어야 Employee* 로 불러도 객체의 진짜 클래스의 pay가 불린다    virtual int pay() const { return base_; }     std::string line() const { return name_ + " " + std::to_string(pay()); } protected:    std::string name_;    int base_;    int hours_ = 0, sold_ = 0;}; class Hourly : public Employee {public:    Hourly(std::string name, int rate) : Employee(std::move(name), 0), rate_(rate) {}     int pay() const override {        int over = std::max(0, hours_ - 160);        return rate_ * (hours_ - over) + 2 * rate_ * over;    } private:    int rate_;}; class Sales : public Employee {public:    Sales(std::string name, int base, int pct) : Employee(std::move(name), base), pct_(pct) {}     int pay() const override { return Employee::pay() + sold_ * pct_ / 100; } private:    int pct_;}; int main() {    std::ios::sync_with_stdio(false);    int n;    std::cin >> n;    // 객체를 값으로 담으면(vector<Employee>) 하위 부분이 잘려 나간다: 포인터로 담는다    std::vector<std::unique_ptr<Employee>> staff;    std::unordered_map<std::string, Employee *> byName;    for (int i = 0; i < n; i++) {        std::string kind, name;        int base;        std::cin >> kind >> name >> base;        if (kind == "staff") staff.push_back(std::make_unique<Employee>(name, base));        else if (kind == "hourly") staff.push_back(std::make_unique<Hourly>(name, base));        else {            int pct;            std::cin >> pct;            staff.push_back(std::make_unique<Sales>(name, base, pct));        }        byName[name] = staff.back().get();    }    int q;    std::cin >> q;    for (int i = 0; i < q; i++) {        std::string cmd, name;        int x;        std::cin >> cmd >> name >> x;        if (cmd == "work") byName[name]->work(x);        else byName[name]->sell(x);    }    int total = 0;    for (const auto &e : staff) {        std::cout << e->line() << '\n';        total += e->pay();    }    std::cout << "total " << total << '\n';}