물건 하나의 처음 가격 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
오류 목록00F8 ↓ · ⇧F8 ↑
입력(stdin)
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 5개 언어 풀이
이 문제는 인터페이스나 그 구실을 하는 문법이 있는 Python·TypeScript·Java·C#·C++만 받아요. JavaScript는 메서드 이름만 맞으면 그냥 부르는 덕 타이핑이라 인터페이스를 적을 곳이 없고, C와 크레이콘에는 클래스가 없어요. "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, abstractmethodclass 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"; }}