{ }codelper
インターフェースP0199

順に使う割引クーポン

品物一つの最初の値段 P ウォンに、クーポン n 枚を与えられた順に一枚ずつ使う。
クーポンは三種類ある。
- "rate p":今の値段の p% を引く。
引く額は、今の値段 × p ÷ 100 の 1 ウォン未満を切り捨てた整数である。
- "cut k":今の値段から k ウォンを引く。
値段が 0 ウォンより小さくなるなら 0 ウォンになる。
- "cap c":今の値段が c ウォンより高ければ c ウォンになり、そうでなければそのままである。
一行目に P と n、続く n 行にクーポンが使う順に一枚ずつ与えられる。
クーポンを一枚使うたびに、その後の値段を一行に一つずつ出力せよ。
値段を受け取りクーポンを使った後の値段を返すメソッド apply を一つだけ持つクーポンのインターフェースを作り、三種類のクーポンのクラスにそれを実装させよう。
クーポンをすべてクーポンのインターフェース型のリストに入れてから、リストを順に回り、種類を聞かずに apply だけを呼んでみよう。

入力
1 ≤ P ≤ 1,000,000、1 ≤ n ≤ 1,000、0 ≤ p ≤ 100、0 ≤ k, c ≤ 1,000,000(すべて整数)
出力
n 行:クーポンを一枚使うたびのその後の値段
例 1 · stdin
12345 4
rate 10
cut 2000
cap 8999
rate 50
stdout
11111
9111
8999
4500
main.py
入力(stdin)

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

$ 準備完了

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

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

この問題はインターフェースか、その役目をする構文がある Python・TypeScript・Java・C#・C++ だけを受け付けます。
JavaScript はメソッド名さえ合えばそのまま呼ぶダックタイピングなのでインターフェースを書く場所がなく、C と Craykon にはクラスがありません。
「apply がある」という約束の書き方は言語ごとに違います。
TypeScript interface Coupon { apply(price: number): number } と class RateCoupon implements Coupon、Java interface Coupon と implements(メソッドは public でなければなりません。
この解答は record で短く書きました)、C# interface ICoupon(名前の前に I を付ける慣習)と class RateCoupon : ICoupon、C++ は純粋仮想関数 virtual int apply(int) const = 0; だけを持つ抽象クラスを継承し、override を付けます。
Python は abc.ABC と @abstractmethod で「このメソッドを必ず作れ」と書きます(書き忘れたクラスはオブジェクトを作るときに TypeError)。
TypeScript のインターフェースは型検査のときだけあり、実行するコードからは消えます。
落とし穴は四つ:① C++ で基底クラスのメソッドに virtual を付け忘れると、Coupon のポインタで呼んだとき派生クラスのものではなく基底クラスのものが呼ばれます(静的束縛)。
Java・C# のインターフェースのメソッドはいつも実装側が呼ばれます。
② Java・C#・C++ で price * (p / 100) のように割合を先に割ると、整数の割り算なので p が 100 未満のとき 0 になり、まったく引かれません。
掛けてから割ります。
③ 切り捨てるのは引く額です。
残る値段 price * (100 - p) / 100 を切り捨てると、999 ウォンの 10% 引きが 900 ウォンではなく 899 ウォンになります。
④ cut で値段が負にならないよう max(0, …) で止めます。

Pythonmain.py · 49 行
import sysfrom abc import ABC, abstractmethod  class Coupon(ABC):    """가격을 받아 쿠폰을 쓴 뒤의 가격을 돌려준다"""     @abstractmethod    def apply(self, price: int) -> int: ...  class RateCoupon(Coupon):    def __init__(self, p: int):        self.p = p     def apply(self, price: int) -> int:        return price - price * self.p // 100  # 깎는 돈의 1원 아래를 버린다  class CutCoupon(Coupon):    def __init__(self, k: int):        self.k = k     def apply(self, price: int) -> int:        return max(0, price - self.k)  class CapCoupon(Coupon):    def __init__(self, c: int):        self.c = c     def apply(self, price: int) -> int:        return min(price, self.c)  KINDS = {"rate": RateCoupon, "cut": CutCoupon, "cap": CapCoupon} lines = sys.stdin.read().split("\n")price, n = map(int, lines[0].split())coupons: list[Coupon] = []for i in range(1, n + 1):    kind, value = lines[i].split()    coupons.append(KINDS[kind](int(value))) out = []for c in coupons:  # 어떤 쿠폰인지 묻지 않고 apply만 부른다    price = c.apply(price)    out.append(price)print("\n".join(map(str, out)))
TypeScriptmain.ts · 49 行
import { readFileSync } from "node:fs"; // 가격을 받아 쿠폰을 쓴 뒤의 가격을 돌려준다interface Coupon {  apply(price: number): number;} class RateCoupon implements Coupon {  constructor(private readonly p: number) {}  apply(price: number): number {    return price - Math.floor((price * this.p) / 100); // 깎는 돈의 1원 아래를 버린다  }} class CutCoupon implements Coupon {  constructor(private readonly k: number) {}  apply(price: number): number {    return Math.max(0, price - this.k);  }} class CapCoupon implements Coupon {  constructor(private readonly c: number) {}  apply(price: number): number {    return Math.min(price, this.c);  }} function makeCoupon(kind: string, value: number): Coupon {  if (kind === "rate") return new RateCoupon(value);  if (kind === "cut") return new CutCoupon(value);  return new CapCoupon(value);} const lines: string[] = readFileSync(0, "utf8").split("\n");const [first, count] = lines[0].trim().split(/\s+/).map(Number);const coupons: Coupon[] = [];for (let i = 1; i <= count; i++) {  const [kind, value] = lines[i].trim().split(/\s+/);  coupons.push(makeCoupon(kind, Number(value)));} let price = first;const out: number[] = [];for (const c of coupons) {  price = c.apply(price); // 어떤 쿠폰인지 묻지 않고 apply만 부른다  out.push(price);}console.log(out.join("\n"));
JavaMain.java · 55 行
import java.io.*;import java.util.*; public class Main {    // 가격을 받아 쿠폰을 쓴 뒤의 가격을 돌려준다    interface Coupon {        int apply(int price);    }     record RateCoupon(int p) implements Coupon {        public int apply(int price) {            return price - price * p / 100; // 곱하고 나서 나눈다: 깎는 돈의 1원 아래를 버린다        }    }     record CutCoupon(int k) implements Coupon {        public int apply(int price) {            return Math.max(0, price - k);        }    }     record CapCoupon(int c) implements Coupon {        public int apply(int price) {            return Math.min(price, c);        }    }     static Coupon makeCoupon(String kind, int value) {        return switch (kind) {            case "rate" -> new RateCoupon(value);            case "cut" -> new CutCoupon(value);            default -> new CapCoupon(value);        };    }     public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(br.readLine());        int price = Integer.parseInt(st.nextToken());        int n = Integer.parseInt(st.nextToken());        List<Coupon> coupons = new ArrayList<>();        for (int i = 0; i < n; i++) {            st = new StringTokenizer(br.readLine());            String kind = st.nextToken();            coupons.add(makeCoupon(kind, Integer.parseInt(st.nextToken())));        }         StringBuilder sb = new StringBuilder();        for (Coupon c : coupons) {            price = c.apply(price); // 어떤 쿠폰인지 묻지 않고 apply만 부른다            sb.append(price).append('\n');        }        System.out.print(sb);    }}
C#Main.cs · 60 行
using System;using System.Collections.Generic;using System.Text; // 가격을 받아 쿠폰을 쓴 뒤의 가격을 돌려준다interface ICoupon{    int Apply(int price);} class RateCoupon : ICoupon{    private readonly int p;    public RateCoupon(int p) { this.p = p; }    public int Apply(int price) => price - price * p / 100; // 곱하고 나서 나눈다: 깎는 돈의 1원 아래를 버린다} class CutCoupon : ICoupon{    private readonly int k;    public CutCoupon(int k) { this.k = k; }    public int Apply(int price) => Math.Max(0, price - k);} class CapCoupon : ICoupon{    private readonly int c;    public CapCoupon(int c) { this.c = c; }    public int Apply(int price) => Math.Min(price, c);} class Program{    static ICoupon MakeCoupon(string kind, int value) => kind switch    {        "rate" => new RateCoupon(value),        "cut" => new CutCoupon(value),        _ => new CapCoupon(value),    };     static void Main()    {        var first = Console.ReadLine()!.Split(' ', StringSplitOptions.RemoveEmptyEntries);        int price = int.Parse(first[0]), n = int.Parse(first[1]);        var coupons = new List<ICoupon>();        for (int i = 0; i < n; i++)        {            var parts = Console.ReadLine()!.Split(' ', StringSplitOptions.RemoveEmptyEntries);            coupons.Add(MakeCoupon(parts[0], int.Parse(parts[1])));        }         var sb = new StringBuilder();        foreach (ICoupon c in coupons)        {            price = c.Apply(price); // 어떤 쿠폰인지 묻지 않고 Apply만 부른다            sb.Append(price).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 51 行
#include <algorithm>#include <iostream>#include <memory>#include <string>#include <vector> // C++의 인터페이스: 순수 가상 함수만 가진 추상 클래스struct Coupon {    virtual int apply(int price) const = 0;    virtual ~Coupon() = default; // 기반 포인터로 지워도 파생 소멸자가 불리게}; struct RateCoupon : Coupon {    int p;    explicit RateCoupon(int p) : p(p) {}    int apply(int price) const override { return price - price * p / 100; } // 깎는 돈의 1원 아래를 버린다}; struct CutCoupon : Coupon {    int k;    explicit CutCoupon(int k) : k(k) {}    int apply(int price) const override { return std::max(0, price - k); }}; struct CapCoupon : Coupon {    int c;    explicit CapCoupon(int c) : c(c) {}    int apply(int price) const override { return std::min(price, c); }}; std::unique_ptr<Coupon> makeCoupon(const std::string& kind, int value) {    if (kind == "rate") return std::make_unique<RateCoupon>(value);    if (kind == "cut") return std::make_unique<CutCoupon>(value);    return std::make_unique<CapCoupon>(value);} int main() {    int price, n;    std::cin >> price >> n;    std::vector<std::unique_ptr<Coupon>> coupons;    for (int i = 0; i < n; i++) {        std::string kind;        int value;        std::cin >> kind >> value;        coupons.push_back(makeCoupon(kind, value));    }    for (const auto& c : coupons) {        price = c->apply(price); // 어떤 쿠폰인지 묻지 않고 apply만 부른다        std::cout << price << "\n";    }}