자판기에 음료 k종류가 있고, i번 음료는 재고가 s_i개, 값이 p_i원이다.
주문 q건을 주어진 차례로 처리한다.
주문 하나는 넣은 돈 c원과 고른 음료 번호 t개로 이루어진다.
한 주문에서 같은 번호를 여러 번 고를 수 있다.
고른 차례대로 하나씩 꺼낸다.
하나를 꺼내면 그 음료의 재고가 1 줄고, 남은 돈이 그 값만큼 줄고, 자판기의 매출이 그 값만큼 는다.
꺼내려는 음료의 재고가 0이거나 남은 돈이 그 값보다 적으면 그 주문은 통째로 취소된다.
취소되면 재고와 매출은 그 주문을 시작하기 전으로 되돌아간다.
주문마다 한 줄씩, 끝까지 꺼냈으면 ok와 남은 돈을, 취소됐으면 fail과 꺼내지 못한 음료가 그 주문에서 몇 번째로 고른 것인지를 출력하라.
모든 주문을 처리한 뒤 한 줄에 1번부터 k번까지 음료의 재고를, 그다음 줄에 매출을 출력하라.
처음 매출은 0원이다.
재고와 매출을 구조체 하나로 묶고, 주문마다 그 구조체를 복사해 복사본에서 꺼내 본 뒤 끝까지 되면 복사본으로 덮어써 풀어 보자.
입력
첫 줄에 k와 q가 빈칸 하나로 나뉘어 주어진다. 둘째 줄에 s_1부터 s_k까지, 셋째 줄에 p_1부터 p_k까지 빈칸 하나로 나뉘어 주어진다. 다음 q줄에 줄마다 c, t, 그리고 고른 음료 번호 t개가 빈칸 하나로 나뉘어 주어진다. 1 ≤ k ≤ 8, 1 ≤ q ≤ 1000, 0 ≤ s_i ≤ 20, 1 ≤ p_i ≤ 1000, 0 ≤ c ≤ 20000, 1 ≤ t ≤ 20, 음료 번호는 1 이상 k 이하
출력
q + 2줄. 주문마다 "ok 남은돈" 또는 "fail 차례"를 빈칸 하나로 띄워 쓴다. 차례는 1부터 센다. 그다음 줄에 재고 k개를 빈칸 하나로 띄워 쓰고, 마지막 줄에 매출을 쓴다
예시 1 · stdin
2 3
2 1
300 500
1000 2 1 2
1000 3 1 1 2
500 1 1
stdout
ok 200
fail 2
ok 200
0 0
1100
예시 2 · stdin
1 2
3
400
1000 3 1 1 1
800 2 1 1
stdout
fail 3
ok 0
1
800
main.py
오류 목록00F8 ↓ · ⇧F8 ↑
입력(stdin)
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 3개 언어 풀이
이 문제는 C·C++·C#만 받아요. 대입하면 데이터가 통째로 복사되는 값 타입 구조체를 직접 만들 수 있는 게 이 셋이고, Python·JavaScript·TypeScript·Java의 객체와 크레이콘의 사전()은 대입하면 같은 것을 함께 가리키는 참조예요. 자판기 상태(재고 배열과 매출)를 구조체 Machine 하나로 묶고, 주문마다 Machine trial = m;으로 복사본을 만들어 거기서 꺼내 봐요. 끝까지 되면 m = trial;로 덮어쓰고, 취소되면 trial을 버리기만 하면 돼서 되돌리는 코드가 따로 없어요. 구조체 대입이 어디까지 복사하는지는 멤버의 타입에 달려 있어요. C는 구조체 안의 배열 int stock[8]을 원소까지 복사해요(배열끼리는 =로 대입할 수 없지만 구조체에 들어 있으면 함께 복사돼요). C++의 vector<int> stock 멤버도 원소까지 복사돼요. C#의 struct는 필드 값을 복사하는데, int[] 필드의 값은 배열을 가리키는 참조예요. 그래서 C# 풀이는 Copy() 메서드에서 (int[])Stock.Clone()으로 배열을 새로 만들어요. 함정 하나: C#에서 Machine trial = m;만 하면 Sales는 따로지만 Stock 배열은 함께 써요. 첫째 예시의 둘째 주문이 취소돼도 1번 음료의 재고가 0으로 남아서 셋째 주문이 fail 1이 되고 매출은 800이 나와요. 함정 둘: C에서 재고를 int *stock(malloc으로 잡은 곳)으로 두면 C#과 똑같이 주소만 복사돼서 같은 틀린 답이 나와요. 함정 셋: C의 Machine *trial = &m;이나 C++의 Machine& trial = m;은 복사가 아니라 m을 가리키는 다른 이름이에요. 취소해도 재고와 매출이 모두 그대로 남아서 첫째 예시의 셋째 주문이 fail 1이 돼요. 함정 넷: 꺼내는 함수가 bool take(Machine t, int x, int money)처럼 구조체를 값으로 받으면 함수 안의 복사본만 바뀌어요. 첫째 예시에서 재고가 줄지 않아 둘째 주문이 fail 3이 되고 마지막에 2 1과 0이 나와요. 제자리에서 바꾸려면 C는 Machine *t로 받아 take(&trial, …)로 부르고, C++은 Machine& t로 받고, C#은 구조체의 메서드로 만들거나 ref Machine t로 받아요.
C#Main.cs · 66 줄
using System;using System.Text;struct Machine{ public int[] Stock; // 배열은 참조다: 구조체를 대입해도 배열 자체는 복사되지 않는다 public int Sales; // 배열까지 새로 만든 복사본 public Machine Copy() { Machine c = this; c.Stock = (int[])Stock.Clone(); return c; } // 메서드는 자기 자신(this)을 제자리에서 바꾼다 public bool Take(int x, int money, int[] price) { if (Stock[x] == 0 || money < price[x]) return false; Stock[x]--; Sales += price[x]; return true; }}class Program{ static void Main() { var data = Console.In.ReadToEnd().Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); int pos = 0; int k = int.Parse(data[pos++]), q = int.Parse(data[pos++]); var m = new Machine { Stock = new int[k], Sales = 0 }; var price = new int[k]; for (int i = 0; i < k; i++) m.Stock[i] = int.Parse(data[pos++]); for (int i = 0; i < k; i++) price[i] = int.Parse(data[pos++]); var sb = new StringBuilder(); for (int o = 0; o < q; o++) { int money = int.Parse(data[pos++]), t = int.Parse(data[pos++]); Machine trial = m.Copy(); int failed = 0; for (int j = 0; j < t; j++) { int x = int.Parse(data[pos + j]) - 1; if (!trial.Take(x, money, price)) { failed = j + 1; break; } money -= price[x]; } pos += t; if (failed > 0) { sb.Append("fail ").Append(failed).Append('\n'); } else { m = trial; sb.Append("ok ").Append(money).Append('\n'); } } sb.Append(string.Join(" ", m.Stock)).Append('\n').Append(m.Sales).Append('\n'); Console.Write(sb); }}
C++main.cpp · 53 줄
#include <iostream>#include <vector>using namespace std;struct Machine { vector<int> stock; // vector 멤버는 구조체를 대입할 때 원소까지 복사된다 int sales = 0;};vector<int> price;// 제자리에서 바꾸려고 참조로 받는다bool take(Machine& t, int x, int money) { if (t.stock[x] == 0 || money < price[x]) return false; t.stock[x]--; t.sales += price[x]; return true;}int main() { int k, q; cin >> k >> q; Machine m; m.stock.resize(k); price.resize(k); for (int& s : m.stock) cin >> s; for (int& p : price) cin >> p; for (int o = 0; o < q; o++) { int money, t; cin >> money >> t; vector<int> picks(t); for (int& x : picks) cin >> x; Machine trial = m; // 재고와 매출이 모두 복사된다 int failed = 0; for (int j = 0; j < t; j++) { int x = picks[j] - 1; if (!take(trial, x, money)) { failed = j + 1; break; } money -= price[x]; } if (failed) { cout << "fail " << failed << '\n'; } else { m = trial; cout << "ok " << money << '\n'; } } for (int i = 0; i < k; i++) cout << (i ? " " : "") << m.stock[i]; cout << '\n' << m.sales << '\n'; return 0;}
Cmain.c · 51 줄
#include <stdio.h>#define MAXK 8#define MAXT 20typedef struct { int stock[MAXK]; // 구조체 안의 배열은 구조체를 대입할 때 통째로 복사된다 int sales;} Machine;int price[MAXK];// 제자리에서 바꾸려고 포인터로 받는다int take(Machine *t, int x, int money) { if (t->stock[x] == 0 || money < price[x]) return 0; t->stock[x]--; t->sales += price[x]; return 1;}int main(void) { int k, q; if (scanf("%d %d", &k, &q) != 2) return 0; Machine m = {{0}, 0}; for (int i = 0; i < k; i++) scanf("%d", &m.stock[i]); for (int i = 0; i < k; i++) scanf("%d", &price[i]); for (int o = 0; o < q; o++) { int money, t, picks[MAXT]; scanf("%d %d", &money, &t); for (int j = 0; j < t; j++) scanf("%d", &picks[j]); Machine trial = m; // 재고 배열과 매출이 모두 복사된다 int failed = 0; for (int j = 0; j < t; j++) { int x = picks[j] - 1; if (!take(&trial, x, money)) { failed = j + 1; break; } money -= price[x]; } if (failed) { printf("fail %d\n", failed); } else { m = trial; printf("ok %d\n", money); } } for (int i = 0; i < k; i++) printf(i ? " %d" : "%d", m.stock[i]); printf("\n%d\n", m.sales); return 0;}