{ }codelper
队列模拟64 位整数P0309

开裂就冷却后重新排队的窑炉队伍

陶艺工坊有k座窑,编号从1到k。
有n件陶器来到工坊。
第i件陶器在第aᵢ秒到达,排到窑前队伍的末尾。
一座窑一次只烧一件陶器,第i件陶器烧一次需要bᵢ秒。
第i件陶器在最初的cᵢ次烧制中每次都会开裂。
开裂的陶器从那次烧制结束的时刻起冷却r秒,然后重新排到队伍末尾。
它的下一次烧制,也就是第cᵢ + 1次,不会开裂,这次烧制结束的时刻就是这件陶器完成的时刻。
一开始所有的窑都是空的。
在同一时刻T,事情按下面的顺序发生。
① 所有在第T秒烧制结束的窑变空。
② 在第T秒冷却结束的陶器排入队伍。
这样的陶器有多件时,从编号较小的窑里出来的先排(冷却时间都相同,所以它们是在同一时刻烧制结束的)。
③ 在第T秒到达的新陶器按输入顺序排入队伍。
④ 只要既有空窑又有排队的陶器,空窑中编号最小的那座就开始烧队首的陶器。
如果r为0,开裂的陶器在烧制结束的那一时刻的②排入队伍。
请输出每件陶器完成的时刻,以及每座窑开始烧制的次数。

输入
第一行给出陶器数n(1 ≤ n ≤ 10,000)、窑数k(1 ≤ k ≤ 8)和冷却时间r(0 ≤ r ≤ 1,000,000,000),用一个空格分隔。
接下来n行的第i行给出整数aᵢ(0 ≤ aᵢ ≤ 1,000,000,000)、bᵢ(1 ≤ bᵢ ≤ 10,000)和cᵢ(0 ≤ cᵢ ≤ 10),用一个空格分隔。
aᵢ按不减的顺序给出(a₁ ≤ a₂ ≤ … ≤ aₙ)。
输出
前n行的第i行输出第i件陶器完成的时刻。
最后一行输出1号窑到k号窑开始烧制的次数,用一个空格分隔。
数字不带逗号。
样例 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
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

这是用两个队列做的模拟。
窑前的队伍显然是队列,关键在于正在冷却的陶器也是一个队列。
冷却时间r对每件陶器都相同,所以先开始冷却的先冷却完。
因此不需要排序或堆,按开裂的先后入队,只看队首即可。
时间不是一秒一秒地数,而是跳到“下一件事发生的时刻”:下一件陶器到达的时刻、冷却队列队首冷却结束的时刻、k座窑结束的时刻中最早的一个。
示例中第0秒陶器1、2进入窑1、2。
第2秒陶器1开裂,冷却到第5秒,排着队的陶器3进入窑1,在第5秒结束。
第5秒只有窑1变空(窑2要烧到第6秒),冷却完的陶器1先排,刚到的陶器4排在后面。
所以陶器1进入窑1,在第7秒完成;陶器4在第6秒进入空出来的窑2,在第7秒完成。
陷阱一:同一时刻冷却完的陶器和新陶器相遇时,冷却完的在前。
顺序反了,示例中陶器4会在第5秒占用窑1,在第6秒结束,而陶器1到第6秒才进窑,在第8秒结束。
每座窑的次数也变成4 1。
陷阱二:开裂的陶器不能立刻排到队尾。
它要过r秒才能排队,其间到达的陶器排在它前面。
陷阱三:同一时刻要先清空窑,再安排队伍。
先安排的话,刚结束的窑会闲到下一件事发生。
陷阱四:有多座空窑时先用编号小的。
完成时刻可能不变,但每座窑的次数会变。
陷阱五:时刻会超过32位整数。
第1,000,000,000秒到达的陶器每次冷却1,000,000,000秒、冷却十次,就超过110亿。
C和C++要用long long,Java和C#要用long;Python的整数以及JavaScript、TypeScript、Craykon的数(2^53以内精确)可以直接用。
陷阱六:陶器总共最多入队110,000次。
Craykon的列表最多10,000项,只追加的列表做不了队列。
同时排队的陶器不超过n件,所以把n格的列表当作环来用,位置用除以n的余数绕回。
不绕回就会指到列表之外而停止。
各语言的队列写法不同。
Python用collections.deque的append和popleft;列表的pop(0)要移动后面所有元素,很慢。
Java用ArrayDeque,C#用Queue<int>,C++用std::queue。
JavaScript和TypeScript没有专门的队列,所以用数组加队首下标来实现;数组很长时shift()可能变慢。
C用数组、队首下标和个数手写环形队列。
Craykon也用新列表(n, 0)建的列表以同样方式实现。

Pythonmain.py · 58 行
import sysfrom collections import deque data = 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] += 1 print("\n".join(map(str, finish)))print(" ".join(map(str, count)))
JavaScriptmain.js · 73 行
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 行
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 行
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 行
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 行
#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 行
#include <stdio.h> #define MAXN 10000#define MAXK 8 static 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 行
첫줄 = 控制台.读数字();n = 첫줄[0];k = 첫줄[1];rest = 첫줄[2];arrive = [];bake = [];left = []; // 앞으로 금이 갈 횟수循环 (i = 0; i < n; i++){    줄 = 控制台.读数字();    arrive.添加(줄[0]);    bake.添加(줄[1]);    left.添加(줄[2]);} end = 新列表(k, -1); // 가마가 굽기를 끝내는 시각(-1이면 비어 있다)cur = 新列表(k, 0); // 가마가 굽고 있는 도자기count = 新列表(k, 0); // 가마마다 구운 횟수ready = 新列表(n, 0); // 식힘이 끝나 다시 줄에 서는 시각finish = 新列表(n, 0); // 큐 두 개를 둥근 목록으로 만든다. 목록은 10,000칸까지라 끝없이 넣기만 할 수 없고,// 한 번에 n개보다 많이 들어 있지 않으니 n칸을 돌려 쓴다cooling = 新列表(n, 0); // 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다coolHead = 0;coolSize = 0;line = 新列表(n, 0); // 가마 앞의 줄lineHead = 0;lineSize = 0; next = 0; // 아직 오지 않은 첫 도자기done = 0;当 (done < n){    // 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것    t = -1;    如果 (next < n)    {        t = arrive[next];    }    如果 (coolSize > 0 && (t < 0 || ready[cooling[coolHead]] < t))    {        t = ready[cooling[coolHead]];    }    循环 (j = 0; j < k; j++)    {        如果 (end[j] >= 0 && (t < 0 || end[j] < t))        {            t = end[j];        }    }    // ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터)    循环 (j = 0; j < k; j++)    {        如果 (end[j] == t)        {            i = cur[j];            end[j] = -1;            如果 (left[i] > 0)            {                left[i] -= 1;                ready[i] = t + rest;                cooling[(coolHead + coolSize) % n] = i;                coolSize += 1;            }            否则            {                finish[i] = t;                done += 1;            }        }    }    // ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다    当 (coolSize > 0 && ready[cooling[coolHead]] == t)    {        line[(lineHead + lineSize) % n] = cooling[coolHead];        lineSize += 1;        coolHead = (coolHead + 1) % n;        coolSize -= 1;    }    当 (next < n && arrive[next] == t)    {        line[(lineHead + lineSize) % n] = next;        lineSize += 1;        next += 1;    }    // ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다    循环 (j = 0; j < k && lineSize > 0; j++)    {        如果 (end[j] < 0)        {            i = line[lineHead];            lineHead = (lineHead + 1) % n;            lineSize -= 1;            cur[j] = i;            end[j] = t + bake[i];            count[j] += 1;        }    }} 遍历 (x 在 finish){    控制台.输出(x);}控制台.输出(count.连接(" "));