{ }codelper
내포(컴프리헨션)겹친 반복P0353

공책과 펜 선물 묶음

문구점에 공책 n권과 펜 m자루가 있고, 저마다 값이 있다.
공책과 펜에는 주어진 차례대로 1번부터 번호를 붙인다. 오늘은 값이 T 이상인 공책만 d만큼 깎아 준다.
값이 T보다 작은 공책은 값이 그대로이다. 공책 한 권과 펜 한 자루를 묶어 선물 묶음을 만든다.
묶음의 값은 깎은 뒤의 공책 값과 펜 값을 더한 값이다. 다음 세 가지를 차례로 출력하라. 첫째, 깎은 뒤의 공책 값 n개를 주어진 차례대로 한 줄에 출력한다. 둘째, 값이 B 이하인 묶음의 수를 한 줄에 출력하고, 이어서 그런 묶음을 한 줄에 하나씩 공책 번호, 펜 번호, 묶음의 값 차례로 출력한다.
묶음은 공책 번호가 작은 것부터, 공책 번호가 같으면 펜 번호가 작은 것부터 출력한다. 셋째, 값이 B 이하인 묶음들의 값을 같은 값은 한 번만, 작은 값부터 한 줄에 출력한다.
그런 묶음이 없으면 -를 출력한다. 한 줄에 수를 여러 개 출력할 때는 빈칸 하나로 띄운다. 이 문제는 목록에서 새 목록을 만드는 짧은 표현(Python의 내포, 다른 언어의 map·filter 등)을 연습하는 문제이지만, 채점은 출력만 보므로 반복문으로 풀어도 된다.

입력
첫 줄에 n, m, B, T, d가 빈칸 하나로 나뉘어 주어진다.
둘째 줄에 공책 값 n개, 셋째 줄에 펜 값 m개가 빈칸 하나로 나뉘어 주어진다.
1 ≤ n, m ≤ 60, 공책 값과 펜 값은 1 이상 100,000 이하, 1 ≤ B ≤ 200,000, 1 ≤ d < T ≤ 100,000.
모든 수는 정수이다
출력
첫 줄에 깎은 뒤의 공책 값 n개, 둘째 줄에 값이 B 이하인 묶음의 수, 이어서 묶음마다 한 줄씩 공책 번호·펜 번호·묶음의 값, 마지막 줄에 서로 다른 묶음 값들(없으면 -).
수는 쉼표 없이 숫자만 출력
예시 1 · stdin
3 2 1300 1000 200
1200 600 1000
300 500
stdout
1000 600 800
5
1 1 1300
2 1 900
2 2 1100
3 1 1100
3 2 1300
900 1100 1300
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

Python의 내포는 목록에서 새 목록을 한 줄로 만들어요.
값을 바꾸는 [p - d if p >= T else p for p in notes], 두 겹으로 돌며 거르는 [... for i ... for j ... if ...], 같은 값을 하나로 모으는 집합 내포 {t for ...}가 이 문제의 세 줄이에요.
다른 언어는 같은 일을 다른 도구로 해요.
JS·TS는 map·flatMap·filter와 new Set, Java는 스트림의 map·flatMap·filter·distinct·sorted, C#은 LINQ의 Select와 from … from … where … select, Distinct·OrderBy를 써요.
C++은 값을 바꿀 때만 transform을 쓰고 두 겹은 for문으로 돌며 std::set에 모아요.
C와 크레이콘에는 이런 표현이 없어서 반복문으로 빈 배열·목록에 하나씩 넣고(크레이콘 넣기), 정렬한 뒤(C qsort, 크레이콘 정렬) 바로 앞과 같은 값을 건너뛰어요.
함정 하나: Python 내포에서 if의 자리가 뜻을 바꿔요.
for 앞의 a if 조건 else b는 값을 고르는 식이라 원소가 모두 남고, for 뒤의 if는 거르는 조건이라 원소가 줄어요.
예시에서 [p - d for p in notes if p >= T]라고 쓰면 값이 600인 공책이 사라져 첫 줄이 1000 800이 돼요.
함정 둘: for를 두 개 쓰면 왼쪽이 바깥 반복이에요.
차례를 바꾸면 펜이 바깥이 되어 예시의 묶음이 2 2 1100보다 3 1 1100이 먼저 나와요.
C# LINQ의 from 두 개도 같은 차례예요.
함정 셋: 중괄호 {...}는 집합, 대괄호 [...]는 목록이에요.
대괄호로 쓰면 같은 값이 그대로 남아 예시의 마지막 줄이 900 1100 1100 1300 1300이 돼요.
함정 넷: JS·TS의 sort()는 비교 함수를 주지 않으면 수를 문자열로 바꿔 사전 차례로 견줘요.
예시의 마지막 줄이 1100 1300 900이 되니 sort((x, y) => x - y)라고 써야 해요.
Python의 sorted와 Java의 sorted, C#의 OrderBy, 크레이콘의 정렬은 수를 크기로 견줘요.

Pythonmain.py · 21 줄
import sys lines = sys.stdin.read().split("\n")n, m, B, T, d = map(int, lines[0].split())notes = list(map(int, lines[1].split()))pens = list(map(int, lines[2].split())) # 조건식은 for 앞에: 모든 공책이 남고 값만 바뀐다sale = [p - d if p >= T else p for p in notes]# for 두 개는 적힌 차례대로 바깥 → 안쪽, 뒤의 if는 거르는 조건pairs = [(i, j, a + b)         for i, a in enumerate(sale, 1)         for j, b in enumerate(pens, 1)         if a + b <= B]# 집합 내포로 같은 값을 하나로 모은다totals = sorted({t for _, _, t in pairs}) out = [" ".join(map(str, sale)), str(len(pairs))]out += [f"{i} {j} {t}" for i, j, t in pairs]out.append(" ".join(map(str, totals)) if totals else "-")print("\n".join(out))
JavaScriptmain.js · 18 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");const [n, m, B, T, d] = lines[0].trim().split(" ").map(Number);const notes = lines[1].trim().split(" ").map(Number);const pens = lines[2].trim().split(" ").map(Number); // map: 모든 공책이 남고 값만 바뀐다const sale = notes.map((p) => (p >= T ? p - d : p));// flatMap으로 두 겹을 한 줄로 펴고, filter로 거른다const pairs = sale  .flatMap((a, i) => pens.map((b, j) => [i + 1, j + 1, a + b]))  .filter(([, , t]) => t <= B);// Set으로 같은 값을 하나로 모으고, 수로 견주어 정렬한다const totals = [...new Set(pairs.map(([, , t]) => t))].sort((x, y) => x - y); const out = [sale.join(" "), String(pairs.length)];for (const [i, j, t] of pairs) out.push(`${i} ${j} ${t}`);out.push(totals.length > 0 ? totals.join(" ") : "-");console.log(out.join("\n"));
TypeScriptmain.ts · 22 줄
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n");const [, , B, T, d]: number[] = lines[0].trim().split(" ").map(Number);const notes: number[] = lines[1].trim().split(" ").map(Number);const pens: number[] = lines[2].trim().split(" ").map(Number); type Pair = [number, number, number]; // map: 모든 공책이 남고 값만 바뀐다const sale: number[] = notes.map((p) => (p >= T ? p - d : p));// flatMap으로 두 겹을 한 줄로 펴고, filter로 거른다const pairs: Pair[] = sale  .flatMap((a, i) => pens.map((b, j): Pair => [i + 1, j + 1, a + b]))  .filter(([, , t]) => t <= B);// Set으로 같은 값을 하나로 모으고, 수로 견주어 정렬한다const totals: number[] = [...new Set(pairs.map(([, , t]) => t))].sort((x, y) => x - y); const out: string[] = [sale.join(" "), String(pairs.length)];for (const [i, j, t] of pairs) out.push(`${i} ${j} ${t}`);out.push(totals.length > 0 ? totals.join(" ") : "-");console.log(out.join("\n"));
JavaMain.java · 41 줄
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.Arrays;import java.util.stream.Collectors;import java.util.stream.IntStream; public class Main {    static int[] readInts(BufferedReader in) throws IOException {        return Arrays.stream(in.readLine().trim().split(" ")).mapToInt(Integer::parseInt).toArray();    }     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int[] head = readInts(in);        int n = head[0], m = head[1], B = head[2], T = head[3], d = head[4];        int[] notes = readInts(in);        int[] pens = readInts(in);         // 스트림의 map: 모든 공책이 남고 값만 바뀐다        int[] sale = Arrays.stream(notes).map(p -> p >= T ? p - d : p).toArray();        // 두 겹은 flatMap으로 펴고 filter로 거른다        int[][] pairs = IntStream.range(0, n).boxed()            .flatMap(i -> IntStream.range(0, m).mapToObj(j -> new int[] {i + 1, j + 1, sale[i] + pens[j]}))            .filter(p -> p[2] <= B)            .toArray(int[][]::new);        // distinct로 같은 값을 하나로 모으고 sorted로 정렬한다        int[] totals = Arrays.stream(pairs).mapToInt(p -> p[2]).distinct().sorted().toArray();         StringBuilder out = new StringBuilder();        out.append(Arrays.stream(sale).mapToObj(String::valueOf).collect(Collectors.joining(" "))).append('\n');        out.append(pairs.length).append('\n');        for (int[] p : pairs) {            out.append(p[0]).append(' ').append(p[1]).append(' ').append(p[2]).append('\n');        }        out.append(totals.length > 0            ? Arrays.stream(totals).mapToObj(String::valueOf).collect(Collectors.joining(" "))            : "-");        System.out.println(out);    }}
C#Main.cs · 34 줄
using System;using System.Linq;using System.Text; class Program{    static int[] ReadInts() =>        Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();     static void Main()    {        int[] head = ReadInts();        int n = head[0], m = head[1], B = head[2], T = head[3], d = head[4];        int[] notes = ReadInts();        int[] pens = ReadInts();         // LINQ Select: 모든 공책이 남고 값만 바뀐다        int[] sale = notes.Select(p => p >= T ? p - d : p).ToArray();        // from 두 개는 적힌 차례대로 바깥 → 안쪽, where는 거르는 조건        var pairs = (from i in Enumerable.Range(0, n)                     from j in Enumerable.Range(0, m)                     where sale[i] + pens[j] <= B                     select (I: i + 1, J: j + 1, Total: sale[i] + pens[j])).ToList();        // Distinct로 같은 값을 하나로 모으고 OrderBy로 정렬한다        int[] totals = pairs.Select(p => p.Total).Distinct().OrderBy(t => t).ToArray();         var sb = new StringBuilder();        sb.Append(string.Join(" ", sale)).Append('\n');        sb.Append(pairs.Count).Append('\n');        foreach (var p in pairs) sb.Append($"{p.I} {p.J} {p.Total}\n");        sb.Append(totals.Length > 0 ? string.Join(" ", totals) : "-").Append('\n');        Console.Write(sb);    }}
C++main.cpp · 48 줄
#include <algorithm>#include <array>#include <iostream>#include <set>#include <vector>using namespace std; int main() {    ios::sync_with_stdio(false);    cin.tie(nullptr);    int n, m, B, T, d;    cin >> n >> m >> B >> T >> d;    vector<int> notes(n), pens(m);    for (int &p : notes) cin >> p;    for (int &p : pens) cin >> p;     // transform: 모든 공책이 남고 값만 바뀐다    vector<int> sale(n);    transform(notes.begin(), notes.end(), sale.begin(),              [&](int p) { return p >= T ? p - d : p; });    // 두 겹은 for문 두 개로 쓴다    vector<array<int, 3>> pairs;    set<int> totals; // 같은 값은 하나만, 늘 정렬된 채로    for (int i = 0; i < n; i++) {        for (int j = 0; j < m; j++) {            int t = sale[i] + pens[j];            if (t <= B) {                pairs.push_back({i + 1, j + 1, t});                totals.insert(t);            }        }    }     for (int i = 0; i < n; i++) cout << (i ? " " : "") << sale[i];    cout << '\n' << pairs.size() << '\n';    for (const auto &p : pairs) cout << p[0] << ' ' << p[1] << ' ' << p[2] << '\n';    if (totals.empty()) {        cout << "-\n";    } else {        bool first = true;        for (int t : totals) {            cout << (first ? "" : " ") << t;            first = false;        }        cout << '\n';    }    return 0;}
Cmain.c · 52 줄
#include <stdio.h>#include <stdlib.h> #define MAXN 60 static int cmp_int(const void *a, const void *b) {    int x = *(const int *)a, y = *(const int *)b;    return (x > y) - (x < y);} int main(void) {    int n, m, B, T, d;    static int sale[MAXN], pens[MAXN];    static int pi[MAXN * MAXN], pj[MAXN * MAXN], pt[MAXN * MAXN], totals[MAXN * MAXN];    scanf("%d %d %d %d %d", &n, &m, &B, &T, &d);    /* C에는 내포가 없다. 읽으면서 바로 값을 바꾼다 */    for (int i = 0; i < n; i++) {        int p;        scanf("%d", &p);        sale[i] = p >= T ? p - d : p;    }    for (int j = 0; j < m; j++) scanf("%d", &pens[j]);     int count = 0;    for (int i = 0; i < n; i++) {        for (int j = 0; j < m; j++) {            if (sale[i] + pens[j] <= B) {                pi[count] = i + 1;                pj[count] = j + 1;                pt[count] = sale[i] + pens[j];                totals[count] = pt[count];                count++;            }        }    }     for (int i = 0; i < n; i++) printf(i ? " %d" : "%d", sale[i]);    printf("\n%d\n", count);    for (int k = 0; k < count; k++) printf("%d %d %d\n", pi[k], pj[k], pt[k]);    if (count == 0) {        printf("-\n");        return 0;    }    /* 정렬한 뒤, 바로 앞과 같은 값은 건너뛴다 */    qsort(totals, (size_t)count, sizeof(int), cmp_int);    for (int k = 0; k < count; k++) {        if (k > 0 && totals[k] == totals[k - 1]) continue;        printf(k ? " %d" : "%d", totals[k]);    }    printf("\n");    return 0;}
Craykonmain.cray · 62 줄
첫줄 = 콘솔.수들읽기();n = 첫줄[0];m = 첫줄[1];B = 첫줄[2];T = 첫줄[3];d = 첫줄[4];notes = 콘솔.수들읽기();pens = 콘솔.수들읽기(); // 크레이콘에는 내포가 없다. 빈 목록에 하나씩 넣는다sale = [];각각 (p 에서 notes){    만약 (p >= T)    {        sale.넣기(p - d);    }    아니면    {        sale.넣기(p);    }}콘솔.쓰기(sale); 줄들 = [];totals = [];반복 (i = 0; i < n; i++){    반복 (j = 0; j < m; j++)    {        t = sale[i] + pens[j];        만약 (t <= B)        {            줄들.넣기(i + 1 + " " + (j + 1) + " " + t);            totals.넣기(t);        }    }}콘솔.쓰기(줄들.개수);각각 (줄 에서 줄들){    콘솔.쓰기(줄);} 만약 (totals.개수 == 0){    콘솔.쓰기("-");}아니면{    // 정렬한 뒤, 바로 앞과 같은 값은 건너뛴다    totals.정렬();    고른것 = [totals[0]];    반복 (k = 1; k < totals.개수; k++)    {        만약 (totals[k] != totals[k - 1])        {            고른것.넣기(totals[k]);        }    }    콘솔.쓰기(고른것);}