The kiln line where cracked pots cool and queue again
A pottery studio has k kilns numbered 1 to k. n pots come to the studio. Pot i arrives at second aᵢ and joins the back of the line in front of the kilns. A kiln fires one pot at a time, and firing pot i once takes bᵢ seconds. Pot i cracks in each of its first cᵢ firings. A cracked pot cools for r seconds from the moment that firing ends, and then joins the back of the line again. Its next firing, the (cᵢ + 1)-th, does not crack it, and the moment that firing ends is the moment the pot is finished. At the start every kiln is empty. At one second T, things happen in this order. ① Every kiln whose firing ends at second T becomes empty. ② The pots whose cooling ends at second T join the line. If there are several, the one that came out of the lower-numbered kiln goes first (the cooling time is the same for all, so their firings ended at the same second). ③ The new pots arriving at second T join the line in input order. ④ While there is both an empty kiln and a pot in line, the lowest-numbered empty kiln starts firing the pot at the front of the line. If r is 0, a cracked pot joins the line in step ② of the very second its firing ends. Print when each pot is finished and how many firings each kiln started.
Input
The first line has the number of pots n (1 ≤ n ≤ 10,000), the number of kilns k (1 ≤ k ≤ 8) and the cooling time r (0 ≤ r ≤ 1,000,000,000), separated by single spaces. Line i of the next n lines has integers aᵢ (0 ≤ aᵢ ≤ 1,000,000,000), bᵢ (1 ≤ bᵢ ≤ 10,000) and cᵢ (0 ≤ cᵢ ≤ 10), separated by single spaces. The aᵢ are given in non-decreasing order (a₁ ≤ a₂ ≤ … ≤ aₙ).
Output
On line i of the first n lines, print the second at which pot i is finished. On the last line, print the number of firings started by kiln 1 through kiln k, separated by single spaces. Print numbers without commas.
Example 1 · stdin
4 2 3
0 2 1
0 6 0
1 3 0
5 1 0
stdout
7
6
5
7
3 2
main.py
Error List00F8 ↓ · ⇧F8 ↑
Input (stdin)
When you run, these lines are fed to input()/stdin in order (the program doesn't stop to ask).
$ Ready
Open this on a computer to write and run code. On mobile you can read the problem and the solutions.
In other languagesShow solutions by language · 8 languages
This is a simulation with two queues. The line in front of the kilns is clearly a queue, but the key is that the cooling pots form a queue too. The cooling time r is the same for every pot, so the pot that starts cooling first finishes cooling first. No sorting or heap is needed: push pots in the order they crack and look only at the front. Time does not tick second by second; it jumps to the next moment something happens: the next arrival, the end of cooling of the front of the cooling queue, or the earliest end among the k kilns. In the example, pots 1 and 2 enter kilns 1 and 2 at second 0. At second 2 pot 1 cracks and cools until second 5, and pot 3 enters kiln 1 and ends at second 5. At second 5 only kiln 1 becomes empty (kiln 2 fires until second 6); the cooled pot 1 joins the line first and the newly arrived pot 4 after it. So pot 1 enters kiln 1 and is finished at second 7, and pot 4 enters kiln 2 when it empties at second 6 and is finished at second 7. Trap 1: when a cooled pot and a new pot come at the same second, the cooled pot goes first. Swapping them lets pot 4 take kiln 1 at second 5 in the example and end at second 6, while pot 1 gets in only at second 6 and ends at second 8. The counts per kiln also change to 4 1. Trap 2: a cracked pot must not go straight to the back of the line. It can join only r seconds later, and pots that arrive in between stand ahead of it. Trap 3: at one second, empty the kilns before filling them. Filling first leaves a kiln that has just ended idle until the next event. Trap 4: when several kilns are empty, use the lowest-numbered one. The finish times may stay the same, but the count per kiln changes. Trap 5: times exceed 32-bit integers. A pot that arrives at second 1,000,000,000 and cools ten times for 1,000,000,000 seconds goes past 11 billion. C and C++ need long long, Java and C# need long; Python integers and the numbers of JavaScript, TypeScript and Craykon (exact up to 2^53) are fine as they are. Trap 6: pots enter the line up to 110,000 times in total. A Craykon list holds at most 10,000 items, so a list that only grows cannot be the queue. At most n pots stand in line at once, so use a list of n slots as a ring and wrap positions with the remainder by n. Without the wrap, the position falls outside the list and the program stops. Each language has its own queue. Python uses append and popleft of collections.deque; pop(0) on a list shifts every later element and is slow. Java uses ArrayDeque, C# uses Queue<int>, and C++ uses std::queue. JavaScript and TypeScript have no built-in queue, so the solution keeps an array and a head index; shift() can be slow on a long array. C builds a ring queue by hand from an array, a head index and a count. Craykon does the same with a list made by NewList(n, 0).
Pythonmain.py · 58 lines
import sysfrom collections import dequedata = sys.stdin.buffer.read().split()n, k, rest = int(data[0]), int(data[1]), int(data[2])arrive = [int(data[3 + 3 * i]) for i in range(n)]bake = [int(data[4 + 3 * i]) for i in range(n)]left = [int(data[5 + 3 * i]) for i in range(n)] # 앞으로 금이 갈 횟수end = [-1] * k # 가마가 굽기를 끝내는 시각(-1이면 비어 있다)cur = [0] * k # 가마가 굽고 있는 도자기count = [0] * k # 가마마다 구운 횟수ready = [0] * n # 식힘이 끝나 다시 줄에 서는 시각finish = [0] * ncooling = deque() # 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다line = deque() # 가마 앞의 줄nxt = 0 # 아직 오지 않은 첫 도자기done = 0while done < n: # 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것 t = -1 if nxt < n: t = arrive[nxt] if cooling and (t < 0 or ready[cooling[0]] < t): t = ready[cooling[0]] for j in range(k): if end[j] >= 0 and (t < 0 or end[j] < t): t = end[j] # ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터) for j in range(k): if end[j] == t: i = cur[j] end[j] = -1 if left[i] > 0: left[i] -= 1 ready[i] = t + rest cooling.append(i) else: finish[i] = t done += 1 # ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다 while cooling and ready[cooling[0]] == t: line.append(cooling.popleft()) while nxt < n and arrive[nxt] == t: line.append(nxt) nxt += 1 # ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다 for j in range(k): if not line: break if end[j] < 0: i = line.popleft() cur[j] = i end[j] = t + bake[i] count[j] += 1print("\n".join(map(str, finish)))print(" ".join(map(str, count)))
JavaScriptmain.js · 73 lines
const data = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [n, k, rest] = data;const arrive = [];const bake = [];const left = []; // 앞으로 금이 갈 횟수for (let i = 0; i < n; i++) { arrive.push(data[3 + 3 * i]); bake.push(data[4 + 3 * i]); left.push(data[5 + 3 * i]);}// 큐: 배열과 머리 번호. shift()는 뒤의 것을 모두 당기니 머리 번호만 옮긴다class Queue { constructor() { this.items = []; this.head = 0; } get size() { return this.items.length - this.head; } front() { return this.items[this.head]; } push(x) { this.items.push(x); } pop() { return this.items[this.head++]; }}const end = new Array(k).fill(-1); // 가마가 굽기를 끝내는 시각(-1이면 비어 있다)const cur = new Array(k).fill(0); // 가마가 굽고 있는 도자기const count = new Array(k).fill(0); // 가마마다 구운 횟수const ready = new Array(n).fill(0); // 식힘이 끝나 다시 줄에 서는 시각const finish = new Array(n).fill(0);const cooling = new Queue(); // 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다const line = new Queue(); // 가마 앞의 줄let next = 0; // 아직 오지 않은 첫 도자기let done = 0;while (done < n) { // 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것 let t = Infinity; if (next < n) t = arrive[next]; if (cooling.size > 0) t = Math.min(t, ready[cooling.front()]); for (let j = 0; j < k; j++) if (end[j] >= 0) t = Math.min(t, end[j]); // ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터) for (let j = 0; j < k; j++) { if (end[j] !== t) continue; const i = cur[j]; end[j] = -1; if (left[i] > 0) { left[i]--; ready[i] = t + rest; cooling.push(i); } else { finish[i] = t; done++; } } // ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다 while (cooling.size > 0 && ready[cooling.front()] === t) line.push(cooling.pop()); while (next < n && arrive[next] === t) line.push(next++); // ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다 for (let j = 0; j < k && line.size > 0; j++) { if (end[j] >= 0) continue; const i = line.pop(); cur[j] = i; end[j] = t + bake[i]; count[j]++; }}console.log(finish.join("\n") + "\n" + count.join(" "));
TypeScriptmain.ts · 73 lines
import { readFileSync } from "node:fs";const data: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [n, k, rest] = data;const arrive: number[] = [];const bake: number[] = [];const left: number[] = []; // 앞으로 금이 갈 횟수for (let i = 0; i < n; i++) { arrive.push(data[3 + 3 * i]); bake.push(data[4 + 3 * i]); left.push(data[5 + 3 * i]);}// 큐: 배열과 머리 번호. shift()는 뒤의 것을 모두 당기니 머리 번호만 옮긴다class Queue<T> { private readonly items: T[] = []; private head = 0; get size(): number { return this.items.length - this.head; } front(): T { return this.items[this.head]; } push(x: T): void { this.items.push(x); } pop(): T { return this.items[this.head++]; }}const end: number[] = new Array<number>(k).fill(-1); // 가마가 굽기를 끝내는 시각(-1이면 비어 있다)const cur: number[] = new Array<number>(k).fill(0); // 가마가 굽고 있는 도자기const count: number[] = new Array<number>(k).fill(0); // 가마마다 구운 횟수const ready: number[] = new Array<number>(n).fill(0); // 식힘이 끝나 다시 줄에 서는 시각const finish: number[] = new Array<number>(n).fill(0);const cooling = new Queue<number>(); // 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다const line = new Queue<number>(); // 가마 앞의 줄let next = 0; // 아직 오지 않은 첫 도자기let done = 0;while (done < n) { // 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것 let t = Infinity; if (next < n) t = arrive[next]; if (cooling.size > 0) t = Math.min(t, ready[cooling.front()]); for (let j = 0; j < k; j++) if (end[j] >= 0) t = Math.min(t, end[j]); // ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터) for (let j = 0; j < k; j++) { if (end[j] !== t) continue; const i: number = cur[j]; end[j] = -1; if (left[i] > 0) { left[i]--; ready[i] = t + rest; cooling.push(i); } else { finish[i] = t; done++; } } // ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다 while (cooling.size > 0 && ready[cooling.front()] === t) line.push(cooling.pop()); while (next < n && arrive[next] === t) line.push(next++); // ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다 for (let j = 0; j < k && line.size > 0; j++) { if (end[j] >= 0) continue; const i: number = line.pop(); cur[j] = i; end[j] = t + bake[i]; count[j]++; }}console.log(finish.join("\n") + "\n" + count.join(" "));
JavaMain.java · 65 lines
import java.io.*;import java.util.*;public class Main { public static void main(String[] args) throws IOException { StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in))); st.nextToken(); int n = (int) st.nval; st.nextToken(); int k = (int) st.nval; st.nextToken(); long rest = (long) st.nval; long[] arrive = new long[n]; int[] bake = new int[n], left = new int[n]; // left: 앞으로 금이 갈 횟수 for (int i = 0; i < n; i++) { st.nextToken(); arrive[i] = (long) st.nval; st.nextToken(); bake[i] = (int) st.nval; st.nextToken(); left[i] = (int) st.nval; } long[] end = new long[k]; // 가마가 굽기를 끝내는 시각(-1이면 비어 있다). 시각은 int를 넘는다 Arrays.fill(end, -1); int[] cur = new int[k]; // 가마가 굽고 있는 도자기 int[] count = new int[k]; // 가마마다 구운 횟수 long[] ready = new long[n]; // 식힘이 끝나 다시 줄에 서는 시각 long[] finish = new long[n]; ArrayDeque<Integer> cooling = new ArrayDeque<>(); // 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다 ArrayDeque<Integer> line = new ArrayDeque<>(); // 가마 앞의 줄 int next = 0, done = 0; while (done < n) { // 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것 long t = Long.MAX_VALUE; if (next < n) t = arrive[next]; if (!cooling.isEmpty()) t = Math.min(t, ready[cooling.peekFirst()]); for (int j = 0; j < k; j++) if (end[j] >= 0) t = Math.min(t, end[j]); // ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터) for (int j = 0; j < k; j++) { if (end[j] != t) continue; int i = cur[j]; end[j] = -1; if (left[i] > 0) { left[i]--; ready[i] = t + rest; cooling.addLast(i); } else { finish[i] = t; done++; } } // ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다 while (!cooling.isEmpty() && ready[cooling.peekFirst()] == t) line.addLast(cooling.pollFirst()); while (next < n && arrive[next] == t) line.addLast(next++); // ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다 for (int j = 0; j < k && !line.isEmpty(); j++) { if (end[j] >= 0) continue; int i = line.pollFirst(); cur[j] = i; end[j] = t + bake[i]; count[j]++; } } StringBuilder sb = new StringBuilder(); for (long x : finish) sb.append(x).append('\n'); for (int j = 0; j < k; j++) sb.append(j > 0 ? " " : "").append(count[j]); System.out.println(sb); }}
C#Main.cs · 75 lines
using System;using System.Collections.Generic;using System.Text;class Program{ static void Main() { var data = Console.In.ReadToEnd().Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); int n = int.Parse(data[0]), k = int.Parse(data[1]); long rest = long.Parse(data[2]); var arrive = new long[n]; var bake = new int[n]; var left = new int[n]; // 앞으로 금이 갈 횟수 for (int i = 0; i < n; i++) { arrive[i] = long.Parse(data[3 + 3 * i]); bake[i] = int.Parse(data[4 + 3 * i]); left[i] = int.Parse(data[5 + 3 * i]); } var end = new long[k]; // 가마가 굽기를 끝내는 시각(-1이면 비어 있다). 시각은 int를 넘는다 Array.Fill(end, -1L); var cur = new int[k]; // 가마가 굽고 있는 도자기 var count = new int[k]; // 가마마다 구운 횟수 var ready = new long[n]; // 식힘이 끝나 다시 줄에 서는 시각 var finish = new long[n]; var cooling = new Queue<int>(); // 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다 var line = new Queue<int>(); // 가마 앞의 줄 int next = 0, done = 0; while (done < n) { // 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것 long t = long.MaxValue; if (next < n) t = arrive[next]; if (cooling.Count > 0) t = Math.Min(t, ready[cooling.Peek()]); for (int j = 0; j < k; j++) if (end[j] >= 0) t = Math.Min(t, end[j]); // ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터) for (int j = 0; j < k; j++) { if (end[j] != t) continue; int i = cur[j]; end[j] = -1; if (left[i] > 0) { left[i]--; ready[i] = t + rest; cooling.Enqueue(i); } else { finish[i] = t; done++; } } // ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다 while (cooling.Count > 0 && ready[cooling.Peek()] == t) line.Enqueue(cooling.Dequeue()); while (next < n && arrive[next] == t) line.Enqueue(next++); // ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다 for (int j = 0; j < k && line.Count > 0; j++) { if (end[j] >= 0) continue; int i = line.Dequeue(); cur[j] = i; end[j] = t + bake[i]; count[j]++; } } var sb = new StringBuilder(); foreach (long x in finish) sb.Append(x).Append('\n'); sb.Append(string.Join(" ", count)).Append('\n'); Console.Write(sb); }}
C++main.cpp · 67 lines
#include <algorithm>#include <iostream>#include <queue>#include <vector>using namespace std;int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, k; long long rest; cin >> n >> k >> rest; vector<long long> arrive(n); vector<int> bake(n), left(n); // left: 앞으로 금이 갈 횟수 for (int i = 0; i < n; i++) cin >> arrive[i] >> bake[i] >> left[i]; vector<long long> end(k, -1); // 가마가 굽기를 끝내는 시각(-1이면 비어 있다). 시각은 int를 넘는다 vector<int> cur(k, 0); // 가마가 굽고 있는 도자기 vector<int> count(k, 0); // 가마마다 구운 횟수 vector<long long> ready(n, 0); // 식힘이 끝나 다시 줄에 서는 시각 vector<long long> finish(n, 0); queue<int> cooling; // 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다 queue<int> line; // 가마 앞의 줄 int next = 0, done = 0; while (done < n) { // 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것 long long t = -1; if (next < n) t = arrive[next]; if (!cooling.empty() && (t < 0 || ready[cooling.front()] < t)) t = ready[cooling.front()]; for (int j = 0; j < k; j++) if (end[j] >= 0 && (t < 0 || end[j] < t)) t = end[j]; // ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터) for (int j = 0; j < k; j++) { if (end[j] != t) continue; int i = cur[j]; end[j] = -1; if (left[i] > 0) { left[i]--; ready[i] = t + rest; cooling.push(i); } else { finish[i] = t; done++; } } // ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다 while (!cooling.empty() && ready[cooling.front()] == t) { line.push(cooling.front()); cooling.pop(); } while (next < n && arrive[next] == t) line.push(next++); // ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다 for (int j = 0; j < k && !line.empty(); j++) { if (end[j] >= 0) continue; int i = line.front(); line.pop(); cur[j] = i; end[j] = t + bake[i]; count[j]++; } } for (long long x : finish) cout << x << '\n'; for (int j = 0; j < k; j++) cout << (j ? " " : "") << count[j]; cout << '\n'; return 0;}
Cmain.c · 82 lines
#include <stdio.h>#define MAXN 10000#define MAXK 8static long long arrive[MAXN], ready[MAXN], finish[MAXN];static int bake[MAXN], left[MAXN]; /* left: 앞으로 금이 갈 횟수 *//* 큐: 둥근 배열. 한 번에 n개보다 많이 들어 있지 않으니 MAXN칸을 돌려 쓴다 */typedef struct { int items[MAXN]; int head, size;} Queue;static void push(Queue *q, int x) { q->items[(q->head + q->size) % MAXN] = x; q->size++;}static int pop(Queue *q) { int x = q->items[q->head]; q->head = (q->head + 1) % MAXN; q->size--; return x;}static Queue cooling; /* 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다 */static Queue line; /* 가마 앞의 줄 */int main(void) { int n, k; long long rest; if (scanf("%d %d %lld", &n, &k, &rest) != 3) return 1; for (int i = 0; i < n; i++) scanf("%lld %d %d", &arrive[i], &bake[i], &left[i]); long long end[MAXK]; /* 가마가 굽기를 끝내는 시각(-1이면 비어 있다). 시각은 int를 넘는다 */ int cur[MAXK], count[MAXK]; for (int j = 0; j < k; j++) { end[j] = -1; cur[j] = 0; count[j] = 0; } int next = 0, done = 0; while (done < n) { /* 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것 */ long long t = -1; if (next < n) t = arrive[next]; if (cooling.size > 0 && (t < 0 || ready[cooling.items[cooling.head]] < t)) t = ready[cooling.items[cooling.head]]; for (int j = 0; j < k; j++) if (end[j] >= 0 && (t < 0 || end[j] < t)) t = end[j]; /* ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터) */ for (int j = 0; j < k; j++) { if (end[j] != t) continue; int i = cur[j]; end[j] = -1; if (left[i] > 0) { left[i]--; ready[i] = t + rest; push(&cooling, i); } else { finish[i] = t; done++; } } /* ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다 */ while (cooling.size > 0 && ready[cooling.items[cooling.head]] == t) push(&line, pop(&cooling)); while (next < n && arrive[next] == t) push(&line, next++); /* ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다 */ for (int j = 0; j < k && line.size > 0; j++) { if (end[j] >= 0) continue; int i = pop(&line); cur[j] = i; end[j] = t + bake[i]; count[j]++; } } for (int i = 0; i < n; i++) printf("%lld\n", finish[i]); for (int j = 0; j < k; j++) printf(j ? " %d" : "%d", count[j]); printf("\n"); return 0;}
Craykonmain.cray · 106 lines
첫줄 = Console.ReadNumbers();n = 첫줄[0];k = 첫줄[1];rest = 첫줄[2];arrive = [];bake = [];left = []; // 앞으로 금이 갈 횟수for (i = 0; i < n; i++){ 줄 = Console.ReadNumbers(); arrive.Add(줄[0]); bake.Add(줄[1]); left.Add(줄[2]);}end = NewList(k, -1); // 가마가 굽기를 끝내는 시각(-1이면 비어 있다)cur = NewList(k, 0); // 가마가 굽고 있는 도자기count = NewList(k, 0); // 가마마다 구운 횟수ready = NewList(n, 0); // 식힘이 끝나 다시 줄에 서는 시각finish = NewList(n, 0);// 큐 두 개를 둥근 목록으로 만든다. 목록은 10,000칸까지라 끝없이 넣기만 할 수 없고,// 한 번에 n개보다 많이 들어 있지 않으니 n칸을 돌려 쓴다cooling = NewList(n, 0); // 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다coolHead = 0;coolSize = 0;line = NewList(n, 0); // 가마 앞의 줄lineHead = 0;lineSize = 0;next = 0; // 아직 오지 않은 첫 도자기done = 0;while (done < n){ // 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것 t = -1; if (next < n) { t = arrive[next]; } if (coolSize > 0 && (t < 0 || ready[cooling[coolHead]] < t)) { t = ready[cooling[coolHead]]; } for (j = 0; j < k; j++) { if (end[j] >= 0 && (t < 0 || end[j] < t)) { t = end[j]; } } // ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터) for (j = 0; j < k; j++) { if (end[j] == t) { i = cur[j]; end[j] = -1; if (left[i] > 0) { left[i] -= 1; ready[i] = t + rest; cooling[(coolHead + coolSize) % n] = i; coolSize += 1; } else { finish[i] = t; done += 1; } } } // ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다 while (coolSize > 0 && ready[cooling[coolHead]] == t) { line[(lineHead + lineSize) % n] = cooling[coolHead]; lineSize += 1; coolHead = (coolHead + 1) % n; coolSize -= 1; } while (next < n && arrive[next] == t) { line[(lineHead + lineSize) % n] = next; lineSize += 1; next += 1; } // ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다 for (j = 0; j < k && lineSize > 0; j++) { if (end[j] < 0) { i = line[lineHead]; lineHead = (lineHead + 1) % n; lineSize -= 1; cur[j] = i; end[j] = t + bake[i]; count[j] += 1; } }}foreach (x in finish){ Console.WriteLine(x);}Console.WriteLine(count.Join(" "));