사진사가 학생 n명을 한 줄로 세워 단체 사진을 찍는다. 학생마다 반 번호가 있고, 같은 반 학생끼리 자리를 바꿔도 같은 줄 세우기로 친다.
그래서 줄 세우기는 반 번호를 늘어놓은 수열 하나로 나타낸다.
가능한 줄 세우기를 모두 사전 순서로 늘어놓는다. 두 줄 세우기를 앞에서부터 견주어 처음으로 다른 자리에서 번호가 작은 쪽이 앞이다.
이 순서는 고리처럼 이어진다. 가장 마지막 줄 세우기(번호가 줄곧 크지 않아지는 것)의 다음은 가장 처음 줄 세우기(번호가 줄곧 작지 않아지는 것)이고, 가장 처음 줄 세우기의 앞은 가장 마지막 줄 세우기다.
지금 줄 세우기에서 m이 양수이면 바로 다음 줄 세우기로 m번 넘어가고, m이 음수이면 바로 앞 줄 세우기로 −m번 되돌아간다. m이 0이면 그대로다.
그렇게 해서 닿는 줄 세우기를 출력하라.
바로 다음 줄 세우기와 바로 앞 줄 세우기를 구하는 일은 각각 함수로 만들고, 수열 하나를 제자리에서 바꾸게 한다.
입력
첫 줄에 T(1 ≤ T ≤ 10). 그다음 줄 세우기마다 두 줄: 첫 줄에 n과 m(1 ≤ n ≤ 1000, −200 ≤ m ≤ 200), 둘째 줄에 반 번호 n개(1 ≤ 번호 ≤ 100)
출력
줄 세우기마다 한 줄: 넘어가거나 되돌아간 뒤의 반 번호 n개를 빈칸 하나로 띄워 출력
예시 1 · stdin
4
3 1
1 3 2
3 2
3 2 1
3 -1
1 2 2
3 -2
1 3 2
stdout
2 1 3
1 3 2
2 2 1
3 2 1
main.py
오류 목록00F8 ↓ · ⇧F8 ↑
입력(stdin)
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이
바로 다음 줄 세우기는 세 걸음이에요. 뒤에서부터 보며 처음으로 a[i] < a[i+1]인 i를 찾고, 그 뒤에서 a[i]보다 큰 수 가운데 가장 뒤의 a[j]와 바꾼 다음, i+1부터 끝까지를 뒤집어요. 바꾼 뒤의 뒤쪽은 늘 내림차순이라, 정렬하지 않고 뒤집기만 해도 가장 작은 꼴이 돼요. i가 없으면(모두 내려가면) 마지막 줄 세우기라 전체를 뒤집으면 첫 줄 세우기예요. 바로 앞 줄 세우기는 비교를 모두 거꾸로 해요. 처음으로 a[i] > a[i+1]인 i를 찾고, 그 뒤에서 a[i]보다 작은 수 가운데 가장 뒤의 것과 바꾼 다음 뒤쪽을 뒤집어요. i가 없으면(모두 오르면) 첫 줄 세우기라 전체를 뒤집으면 마지막이에요. 언어마다 '일부만 뒤집기'가 달라요. C++ std::reverse(a.begin() + i + 1, a.end())는 반열린 구간이고, 표준 라이브러리 std::next_permutation·std::prev_permutation이 이 일을 통째로 해 줘요(끝에 닿으면 반대쪽 끝으로 바꾸고 false를 돌려줘요). C# Array.Reverse(a, 시작, 개수)의 셋째 값은 개수라서 끝 자리를 주면 범위를 넘어 ArgumentException이 나요. Java는 Collections.reverse(list.subList(i + 1, n))가 원래 목록을 뒤집지만 int[]에는 없어서 두 끝을 바꾸며 좁혀 와요. C도 그렇게 해요. 크레이콘 목록.뒤집기()는 목록 전체만 뒤집어서, 구간은 두 끝을 바꾸는 동안 반복으로 뒤집어요. 함정 여섯: ① Python a[i + 1:].reverse()와 JS·TS a.slice(i + 1).reverse()는 복사본을 뒤집어서 원래 a는 그대로예요. Python은 a[i + 1:] = a[i + 1:][::-1]처럼 다시 넣어요. ② 오르는 곳을 a[i] > a[i+1]로 끊으면 같은 번호(2 2)를 오른다고 봐서, 반 번호가 겹칠 때 줄 세우기를 건너뛰거나 되풀이해요. 같으면 계속 왼쪽으로 가야 해요(>=). 앞 줄 세우기에서도 같으면 계속 가야 해요(<=). ③ 바꿀 수를 i 바로 뒤부터 끝 쪽으로 찾으면 '가장 조금 큰 수'가 아니라 처음 만난 큰 수와 바꿔서 여러 줄 세우기를 건너뛰어요. ④ 마지막 줄 세우기에서 아무것도 안 하면 처음으로 돌아가지 않아요. ⑤ 앞 줄 세우기를 다음 줄 세우기에서 베끼며 '큰 수와 바꾸기'를 그대로 두면 엉뚱한 줄이 돼요. ⑥ for (k = 0; k < m; k++)만 쓰면 m이 음수일 때 한 번도 돌지 않아요.
Pythonmain.py · 38 줄
def next_lineup(a): """a를 사전 순으로 바로 다음 줄 세우기로 바꾼다(마지막이면 첫 줄 세우기로).""" i = len(a) - 2 while i >= 0 and a[i] >= a[i + 1]: # 뒤에서부터 처음으로 오르는 곳 i -= 1 if i < 0: # 모두 내려가면 마지막 줄 세우기 → 뒤집으면 첫 줄 세우기 a.reverse() return j = len(a) - 1 while a[j] <= a[i]: # a[i]보다 큰 수 가운데 가장 뒤의 것 j -= 1 a[i], a[j] = a[j], a[i] a[i + 1:] = a[i + 1:][::-1] # 뒤쪽 구간만 제자리에서 뒤집는다def prev_lineup(a): """a를 사전 순으로 바로 앞 줄 세우기로 바꾼다(첫 줄 세우기이면 마지막으로). 비교 방향만 거꾸로다.""" i = len(a) - 2 while i >= 0 and a[i] <= a[i + 1]: # 뒤에서부터 처음으로 내려가는 곳 i -= 1 if i < 0: # 모두 오르면 첫 줄 세우기 → 뒤집으면 마지막 줄 세우기 a.reverse() return j = len(a) - 1 while a[j] >= a[i]: # a[i]보다 작은 수 가운데 가장 뒤의 것 j -= 1 a[i], a[j] = a[j], a[i] a[i + 1:] = a[i + 1:][::-1]t = int(input())for _ in range(t): n, m = map(int, input().split()) a = list(map(int, input().split())) step = next_lineup if m > 0 else prev_lineup # m이 음수면 |m|번 앞으로 for _ in range(abs(m)): step(a) print(*a)
JavaScriptmain.js · 48 줄
const data = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);// 구간 [lo, hi]를 제자리에서 뒤집는다function reverseRange(a, lo, hi) { for (; lo < hi; lo++, hi--) [a[lo], a[hi]] = [a[hi], a[lo]];}// a를 사전 순으로 바로 다음 줄 세우기로 바꾼다(마지막이면 첫 줄 세우기로)function nextLineup(a) { let i = a.length - 2; while (i >= 0 && a[i] >= a[i + 1]) i--; // 뒤에서부터 처음으로 오르는 곳 if (i < 0) { a.reverse(); // 모두 내려가면 마지막 → 뒤집으면 첫 줄 세우기 return; } let j = a.length - 1; while (a[j] <= a[i]) j--; // a[i]보다 큰 수 가운데 가장 뒤의 것 [a[i], a[j]] = [a[j], a[i]]; reverseRange(a, i + 1, a.length - 1);}// a를 사전 순으로 바로 앞 줄 세우기로 바꾼다(첫 줄 세우기이면 마지막으로). 비교 방향만 거꾸로다function prevLineup(a) { let i = a.length - 2; while (i >= 0 && a[i] <= a[i + 1]) i--; // 뒤에서부터 처음으로 내려가는 곳 if (i < 0) { a.reverse(); // 모두 오르면 첫 줄 세우기 → 뒤집으면 마지막 return; } let j = a.length - 1; while (a[j] >= a[i]) j--; // a[i]보다 작은 수 가운데 가장 뒤의 것 [a[i], a[j]] = [a[j], a[i]]; reverseRange(a, i + 1, a.length - 1);}let p = 0;const t = data[p++];const out = [];for (let c = 0; c < t; c++) { const n = data[p++]; const m = data[p++]; const a = data.slice(p, p + n); p += n; const step = m > 0 ? nextLineup : prevLineup; // m이 음수면 |m|번 앞으로 for (let k = 0; k < Math.abs(m); k++) step(a); out.push(a.join(" "));}console.log(out.join("\n"));
TypeScriptmain.ts · 49 줄
import { readFileSync } from "node:fs";const data: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);// 구간 [lo, hi]를 제자리에서 뒤집는다function reverseRange(a: number[], lo: number, hi: number): void { for (; lo < hi; lo++, hi--) [a[lo], a[hi]] = [a[hi], a[lo]];}// a를 사전 순으로 바로 다음 줄 세우기로 바꾼다(마지막이면 첫 줄 세우기로)function nextLineup(a: number[]): void { let i = a.length - 2; while (i >= 0 && a[i] >= a[i + 1]) i--; // 뒤에서부터 처음으로 오르는 곳 if (i < 0) { a.reverse(); // 모두 내려가면 마지막 → 뒤집으면 첫 줄 세우기 return; } let j = a.length - 1; while (a[j] <= a[i]) j--; // a[i]보다 큰 수 가운데 가장 뒤의 것 [a[i], a[j]] = [a[j], a[i]]; reverseRange(a, i + 1, a.length - 1);}// a를 사전 순으로 바로 앞 줄 세우기로 바꾼다(첫 줄 세우기이면 마지막으로). 비교 방향만 거꾸로다function prevLineup(a: number[]): void { let i = a.length - 2; while (i >= 0 && a[i] <= a[i + 1]) i--; // 뒤에서부터 처음으로 내려가는 곳 if (i < 0) { a.reverse(); // 모두 오르면 첫 줄 세우기 → 뒤집으면 마지막 return; } let j = a.length - 1; while (a[j] >= a[i]) j--; // a[i]보다 작은 수 가운데 가장 뒤의 것 [a[i], a[j]] = [a[j], a[i]]; reverseRange(a, i + 1, a.length - 1);}let p = 0;const t = data[p++];const out: string[] = [];for (let c = 0; c < t; c++) { const n = data[p++]; const m = data[p++]; const a = data.slice(p, p + n); p += n; const step = m > 0 ? nextLineup : prevLineup; // m이 음수면 |m|번 앞으로 for (let k = 0; k < Math.abs(m); k++) step(a); out.push(a.join(" "));}console.log(out.join("\n"));
JavaMain.java · 64 줄
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.StringTokenizer;public class Main { static void swap(int[] a, int x, int y) { int tmp = a[x]; a[x] = a[y]; a[y] = tmp; } // 구간 [lo, hi]를 제자리에서 뒤집는다 static void reverseRange(int[] a, int lo, int hi) { for (; lo < hi; lo++, hi--) swap(a, lo, hi); } // a를 사전 순으로 바로 다음 줄 세우기로 바꾼다(마지막이면 첫 줄 세우기로) static void nextLineup(int[] a) { int i = a.length - 2; while (i >= 0 && a[i] >= a[i + 1]) i--; // 뒤에서부터 처음으로 오르는 곳 if (i < 0) { reverseRange(a, 0, a.length - 1); // 마지막 → 뒤집으면 첫 줄 세우기 return; } int j = a.length - 1; while (a[j] <= a[i]) j--; // a[i]보다 큰 수 가운데 가장 뒤의 것 swap(a, i, j); reverseRange(a, i + 1, a.length - 1); } // a를 사전 순으로 바로 앞 줄 세우기로 바꾼다(첫 줄 세우기이면 마지막으로). 비교 방향만 거꾸로다 static void prevLineup(int[] a) { int i = a.length - 2; while (i >= 0 && a[i] <= a[i + 1]) i--; // 뒤에서부터 처음으로 내려가는 곳 if (i < 0) { reverseRange(a, 0, a.length - 1); // 첫 줄 세우기 → 뒤집으면 마지막 return; } int j = a.length - 1; while (a[j] >= a[i]) j--; // a[i]보다 작은 수 가운데 가장 뒤의 것 swap(a, i, j); reverseRange(a, i + 1, a.length - 1); } public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); int t = Integer.parseInt(in.readLine().trim()); StringBuilder out = new StringBuilder(); for (int c = 0; c < t; c++) { StringTokenizer st = new StringTokenizer(in.readLine()); int n = Integer.parseInt(st.nextToken()); int m = Integer.parseInt(st.nextToken()); int[] a = new int[n]; st = new StringTokenizer(in.readLine()); for (int k = 0; k < n; k++) a[k] = Integer.parseInt(st.nextToken()); for (int k = 0; k < m; k++) nextLineup(a); for (int k = 0; k < -m; k++) prevLineup(a); // m이 음수면 |m|번 앞으로 for (int k = 0; k < n; k++) out.append(k > 0 ? " " : "").append(a[k]); out.append('\n'); } System.out.print(out); }}
C#Main.cs · 55 줄
using System;using System.Linq;using System.Text;class Program{ // a를 사전 순으로 바로 다음 줄 세우기로 바꾼다(마지막이면 첫 줄 세우기로) static void NextLineup(int[] a) { int i = a.Length - 2; while (i >= 0 && a[i] >= a[i + 1]) i--; // 뒤에서부터 처음으로 오르는 곳 if (i < 0) { Array.Reverse(a); // 마지막 → 뒤집으면 첫 줄 세우기 return; } int j = a.Length - 1; while (a[j] <= a[i]) j--; // a[i]보다 큰 수 가운데 가장 뒤의 것 (a[i], a[j]) = (a[j], a[i]); // Array.Reverse(배열, 시작, 개수) — 셋째 값은 끝 자리가 아니라 개수다 Array.Reverse(a, i + 1, a.Length - (i + 1)); } // a를 사전 순으로 바로 앞 줄 세우기로 바꾼다(첫 줄 세우기이면 마지막으로). 비교 방향만 거꾸로다 static void PrevLineup(int[] a) { int i = a.Length - 2; while (i >= 0 && a[i] <= a[i + 1]) i--; // 뒤에서부터 처음으로 내려가는 곳 if (i < 0) { Array.Reverse(a); // 첫 줄 세우기 → 뒤집으면 마지막 return; } int j = a.Length - 1; while (a[j] >= a[i]) j--; // a[i]보다 작은 수 가운데 가장 뒤의 것 (a[i], a[j]) = (a[j], a[i]); Array.Reverse(a, i + 1, a.Length - (i + 1)); } static void Main() { int t = int.Parse(Console.ReadLine()!.Trim()); var output = new StringBuilder(); for (int c = 0; c < t; c++) { int[] nm = Console.ReadLine()!.Trim().Split(' ').Select(int.Parse).ToArray(); int[] a = Console.ReadLine()!.Trim().Split(' ').Select(int.Parse).ToArray(); int m = nm[1]; for (int k = 0; k < m; k++) NextLineup(a); for (int k = 0; k < -m; k++) PrevLineup(a); // m이 음수면 |m|번 앞으로 output.Append(string.Join(" ", a)).Append('\n'); } Console.Write(output); }}
C++main.cpp · 49 줄
#include <algorithm>#include <iostream>#include <vector>// a를 사전 순으로 바로 다음 줄 세우기로 바꾼다(마지막이면 첫 줄 세우기로)// (표준 라이브러리 std::next_permutation이 바로 이 일을 한다 — 여기서는 직접 쓴다)void nextLineup(std::vector<int>& a) { int i = (int)a.size() - 2; while (i >= 0 && a[i] >= a[i + 1]) i--; // 뒤에서부터 처음으로 오르는 곳 if (i < 0) { std::reverse(a.begin(), a.end()); // 마지막 → 뒤집으면 첫 줄 세우기 return; } int j = (int)a.size() - 1; while (a[j] <= a[i]) j--; // a[i]보다 큰 수 가운데 가장 뒤의 것 std::swap(a[i], a[j]); std::reverse(a.begin() + i + 1, a.end()); // [시작, 끝) 반열린 구간}// a를 사전 순으로 바로 앞 줄 세우기로 바꾼다(첫 줄 세우기이면 마지막으로). 비교 방향만 거꾸로다// (std::prev_permutation이 이 일을 한다)void prevLineup(std::vector<int>& a) { int i = (int)a.size() - 2; while (i >= 0 && a[i] <= a[i + 1]) i--; // 뒤에서부터 처음으로 내려가는 곳 if (i < 0) { std::reverse(a.begin(), a.end()); // 첫 줄 세우기 → 뒤집으면 마지막 return; } int j = (int)a.size() - 1; while (a[j] >= a[i]) j--; // a[i]보다 작은 수 가운데 가장 뒤의 것 std::swap(a[i], a[j]); std::reverse(a.begin() + i + 1, a.end());}int main() { std::ios::sync_with_stdio(false); int t; std::cin >> t; while (t--) { int n, m; std::cin >> n >> m; std::vector<int> a(n); for (int& x : a) std::cin >> x; for (int k = 0; k < m; k++) nextLineup(a); for (int k = 0; k < -m; k++) prevLineup(a); // m이 음수면 |m|번 앞으로 for (int k = 0; k < n; k++) std::cout << (k ? " " : "") << a[k]; std::cout << '\n'; }}
Cmain.c · 56 줄
#include <stdio.h>static void swap(int *x, int *y) { int tmp = *x; *x = *y; *y = tmp;}/* 구간 [lo, hi]를 제자리에서 뒤집는다 */static void reverse_range(int *a, int lo, int hi) { for (; lo < hi; lo++, hi--) swap(&a[lo], &a[hi]);}/* a를 사전 순으로 바로 다음 줄 세우기로 바꾼다(마지막이면 첫 줄 세우기로) */static void next_lineup(int *a, int n) { int i = n - 2; while (i >= 0 && a[i] >= a[i + 1]) i--; /* 뒤에서부터 처음으로 오르는 곳 */ if (i < 0) { reverse_range(a, 0, n - 1); /* 마지막 → 뒤집으면 첫 줄 세우기 */ return; } int j = n - 1; while (a[j] <= a[i]) j--; /* a[i]보다 큰 수 가운데 가장 뒤의 것 */ swap(&a[i], &a[j]); reverse_range(a, i + 1, n - 1);}/* a를 사전 순으로 바로 앞 줄 세우기로 바꾼다(첫 줄 세우기이면 마지막으로). 비교 방향만 거꾸로다 */static void prev_lineup(int *a, int n) { int i = n - 2; while (i >= 0 && a[i] <= a[i + 1]) i--; /* 뒤에서부터 처음으로 내려가는 곳 */ if (i < 0) { reverse_range(a, 0, n - 1); /* 첫 줄 세우기 → 뒤집으면 마지막 */ return; } int j = n - 1; while (a[j] >= a[i]) j--; /* a[i]보다 작은 수 가운데 가장 뒤의 것 */ swap(&a[i], &a[j]); reverse_range(a, i + 1, n - 1);}int main(void) { int t; scanf("%d", &t); static int a[1000]; while (t--) { int n, m; scanf("%d %d", &n, &m); for (int k = 0; k < n; k++) scanf("%d", &a[k]); for (int k = 0; k < m; k++) next_lineup(a, n); for (int k = 0; k < -m; k++) prev_lineup(a, n); /* m이 음수면 |m|번 앞으로 */ for (int k = 0; k < n; k++) printf(k ? " %d" : "%d", a[k]); printf("\n"); } return 0;}
Craykonmain.cray · 77 줄
// 구간 [lo, hi]를 제자리에서 뒤집는다(목록.뒤집기()는 목록 전체만 뒤집는다)구간뒤집기(a, lo, hi){ 동안 (lo < hi) { tmp = a[lo]; a[lo] = a[hi]; a[hi] = tmp; lo += 1; hi -= 1; }}// a를 사전 순으로 바로 다음 줄 세우기로 바꾼다(마지막이면 첫 줄 세우기로)다음줄(a){ i = a.개수 - 2; 동안 (i >= 0 && a[i] >= a[i + 1]) { i -= 1; } 만약 (i < 0) { a.뒤집기(); // 마지막 → 뒤집으면 첫 줄 세우기 돌려주기; } j = a.개수 - 1; 동안 (a[j] <= a[i]) { j -= 1; } tmp = a[i]; a[i] = a[j]; a[j] = tmp; 구간뒤집기(a, i + 1, a.개수 - 1);}// a를 사전 순으로 바로 앞 줄 세우기로 바꾼다(첫 줄 세우기이면 마지막으로). 비교 방향만 거꾸로다앞줄(a){ i = a.개수 - 2; 동안 (i >= 0 && a[i] <= a[i + 1]) { i -= 1; } 만약 (i < 0) { a.뒤집기(); // 첫 줄 세우기 → 뒤집으면 마지막 돌려주기; } j = a.개수 - 1; 동안 (a[j] >= a[i]) { j -= 1; } tmp = a[i]; a[i] = a[j]; a[j] = tmp; 구간뒤집기(a, i + 1, a.개수 - 1);}t = 콘솔.줄읽기().공백제거().수로변환();반복 (c = 0; c < t; c++){ nm = 콘솔.수들읽기(); a = 콘솔.수들읽기(); m = nm[1]; 반복 (k = 0; k < m; k++) { 다음줄(a); } 반복 (k = 0; k < -m; k++) { 앞줄(a); } 콘솔.쓰기(a.잇기(" "));}