{ }codelper
キューシミュレーションP0071

切符売り場の行列

n 人が前から 1 番から n 番まで順に切符売り場に並んでいる。
i 番の人は切符を tᵢ 枚買いたい。
売り場は一回に行列の先頭の一人に切符を最大 m 枚売る(残りの必要枚数が m より少なければその分だけ)。
買ったあとまだ必要なら、その人は行列の最後尾に並び直し、全部買えたら行列を離れる。
行列が空になるまでこれを繰り返すとき、行列を離れる順に番号を空白区切りで一行に出力せよ。

入力
一行目に n と m、二行目に t₁ … tₙ。
1 ≤ n ≤ 1,000、1 ≤ m ≤ 1,000、1 ≤ tᵢ ≤ 1,000
出力
離れる順の番号 n 個(一行、空白区切り)
例 1 · stdin
3 3
4 3 1
stdout
2 3 1
例 2 · stdin
5 2
5 1 4 2 3
stdout
2 4 3 5 1
main.py
入力(stdin)

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

$ 準備完了

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

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

行列は前から取り出して後ろに入れるキューです。
回数は最大 100 万回なので、そのとおりにシミュレーションすれば大丈夫です。
キューの使い方は言語ごとに違います:Python collections.deque の popleft()(リストの pop(0) は後ろを全部詰めるので遅い)、Java ArrayDeque の pollFirst()/addLast()、C# Queue<int> の Dequeue()/Enqueue()、C++ std::queue の front()/pop()/push()。
JavaScript・TypeScript にはキューがなく shift() を使いがちですが、これも後ろを詰めます — 配列には追加だけして先頭位置(head)を進めます。
C はサイズ n の配列を環状キュー(head = (head + 1) % n)として使います。
行列はいつも n 人以下なので n 個で足ります。
Craykon の解答は 新リスト(n, 0) で環状キューを作ります。
位置で取り除く(0) も後ろを全部詰めますが、ここのように列が 1,000 人以下なら 100 万回でも十分速いです。
罠は二つ:先頭ではなく最後尾から取り出す(スタック)と順番が逆になります。
そして並び直すかどうかは売ったあとに残り枚数が 0 より大きいかで決めます — 売る前に「m 枚以上必要なら並び直す」と聞くと、ちょうど m 枚必要な人が一周余計に回ります。

Pythonmain.py · 16 行
from collections import deque n, m = map(int, input().split())need = list(map(int, input().split())) line = deque(range(n))  # 줄에 선 사람의 번호(0부터)left = need[:]          # 아직 사야 할 표order = []while line:    person = line.popleft()    left[person] -= min(m, left[person])    if left[person] > 0:        line.append(person)  # 더 필요하면 맨 뒤로    else:        order.append(person + 1)print(*order)
JavaScriptmain.js · 15 行
const [n, m, ...need] = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number); // 배열 + 앞 자리(head)로 큐를 만든다. shift()는 뒤의 것을 모두 당겨서 느릴 수 있다.const line = [];for (let i = 0; i < n; i++) line.push(i);let head = 0;const left = need.slice();const order = [];while (head < line.length) {  const person = line[head++];  left[person] -= Math.min(m, left[person]);  if (left[person] > 0) line.push(person); // 더 필요하면 맨 뒤로  else order.push(person + 1);}console.log(order.join(" "));
TypeScriptmain.ts · 17 行
import { readFileSync } from "node:fs"; const [n, m, ...need]: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number); // 배열 + 앞 자리(head)로 큐를 만든다. shift()는 뒤의 것을 모두 당겨서 느릴 수 있다.const line: number[] = [];for (let i = 0; i < n; i++) line.push(i);let head = 0;const left: number[] = need.slice();const order: number[] = [];while (head < line.length) {  const person = line[head++];  left[person] -= Math.min(m, left[person]);  if (left[person] > 0) line.push(person); // 더 필요하면 맨 뒤로  else order.push(person + 1);}console.log(order.join(" "));
JavaMain.java · 30 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayDeque;import java.util.StringJoiner;import java.util.StringTokenizer; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(in.readLine());        int n = Integer.parseInt(st.nextToken());        int m = Integer.parseInt(st.nextToken());        int[] left = new int[n];        ArrayDeque<Integer> line = new ArrayDeque<>();        st = new StringTokenizer(in.readLine());        for (int i = 0; i < n; i++) {            left[i] = Integer.parseInt(st.nextToken());            line.addLast(i);        }        StringJoiner order = new StringJoiner(" ");        while (!line.isEmpty()) {            int person = line.pollFirst();            left[person] -= Math.min(m, left[person]);            if (left[person] > 0) line.addLast(person); // 더 필요하면 맨 뒤로            else order.add(String.valueOf(person + 1));        }        System.out.println(order);    }}
C#Main.cs · 22 行
using System;using System.Collections.Generic;using System.Linq; class Program{    static void Main()    {        int m = int.Parse(Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries)[1]);        int[] left = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();        var line = new Queue<int>(Enumerable.Range(0, left.Length));        var order = new List<int>();        while (line.Count > 0)        {            int person = line.Dequeue();            left[person] -= Math.Min(m, left[person]);            if (left[person] > 0) line.Enqueue(person); // 더 필요하면 맨 뒤로            else order.Add(person + 1);        }        Console.WriteLine(string.Join(" ", order));    }}
C++main.cpp · 28 行
#include <algorithm>#include <iostream>#include <queue>#include <vector> int main() {    int n, m;    std::cin >> n >> m;    std::vector<int> left(n);    std::queue<int> line;    for (int i = 0; i < n; i++) {        std::cin >> left[i];        line.push(i);    }    bool first = true;    while (!line.empty()) {        int person = line.front();        line.pop();        left[person] -= std::min(m, left[person]);        if (left[person] > 0) {            line.push(person); // 더 필요하면 맨 뒤로        } else {            std::cout << (first ? "" : " ") << person + 1;            first = false;        }    }    std::cout << "\n";}
Cmain.c · 31 行
#include <stdio.h> #define MAXN 1000 /* 크기 MAXN인 원형 큐: 줄에는 많아야 n명만 있다 */static int queue[MAXN];static int left[MAXN]; int main(void) {    int n, m;    scanf("%d %d", &n, &m);    for (int i = 0; i < n; i++) {        scanf("%d", &left[i]);        queue[i] = i;    }    int head = 0, size = n, printed = 0;    while (size > 0) {        int person = queue[head];        head = (head + 1) % MAXN;        size--;        left[person] -= left[person] < m ? left[person] : m;        if (left[person] > 0) {            queue[(head + size) % MAXN] = person; /* 더 필요하면 맨 뒤로 */            size++;        } else {            printf(printed++ ? " %d" : "%d", person + 1);        }    }    putchar('\n');    return 0;}
Craykonmain.cray · 32 行
첫줄 = コンソール.数読み();n = 첫줄[0];m = 첫줄[1];left = コンソール.数読み(); // 원형 큐: 줄에는 많아야 n명이라 n칸이면 된다. head는 맨 앞 자리, size는 줄 길이.line = 新リスト(n, 0);繰り返す (i = 0; i < n; i++){    line[i] = i;}head = 0;size = n;order = [];間 (size > 0){    person = line[head];    head = (head + 1) % n;    size -= 1;    left[person] -= 数学.小さい方(m, left[person]);    もし (left[person] > 0)    {        // 더 필요하면 맨 뒤로        line[(head + size) % n] = person;        size += 1;    }    でなければ    {        order.追加(person + 1);    }}コンソール.書く(order);