{ }codelper
类方法P0157

不同员工类型的工资

公司的员工有三类,根据一个月的记录计算工资。
每个员工除了名字,还记录本月工作时间的总和与销售金额的总和。
普通员工(staff)的工资是基本工资。
小时工(hourly)的工资是:160 小时以内每小时得时薪,超过 160 小时的部分每小时得时薪的 2 倍。
销售员(sales)的工资是基本工资加提成,提成是销售金额总和乘以比例 p 再除以 100,舍去小数部分后的整数。
工作时间和销售金额可能记录在任何类型的员工身上,但计算工资只用上面写到的部分。
第一行给出员工数 n,接下来 n 行每行以“staff 名字 基本工资”“hourly 名字 时薪”“sales 名字 基本工资 p”之一给出一名员工。
下一行给出记录数 q,接下来 q 行以“work 名字 小时数”或“sell 名字 金额”给出记录。
按输入顺序每行输出一名员工的“名字 工资”,最后一行输出“total 工资总和”。
把普通员工写成基类 Employee,把小时工和销售员写成它的子类,并重写(override)工资方法 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> 按值存放,子类对象会被切掉(slicing),只剩基类部分。
要存 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 除法对非负数会自动舍去)。
另外,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';}