{ }codelper
内包表記二重ループP0353

ノートとペンのギフトセット

文房具店にノートが n 冊、ペンが m 本あり、それぞれに値段がある。
ノートとペンには与えられた順に 1 番から番号を付ける。
今日は値段が T 以上のノートだけ d 引きになる。
値段が T より安いノートは値段がそのままである。
ノート 1 冊とペン 1 本を組にしてギフトセットを作る。
セットの値段は、値引き後のノートの値段とペンの値段を足した値である。
次の 3 つを順に出力せよ。
1 つ目に、値引き後のノートの値段 n 個を、与えられた順に 1 行に出力する。
2 つ目に、値段が B 以下のセットの数を 1 行に出力し、続けてそのようなセットを 1 行に 1 つずつ、ノートの番号、ペンの番号、セットの値段の順に出力する。
セットはノートの番号が小さいものから、ノートの番号が同じならペンの番号が小さいものから出力する。
3 つ目に、値段が B 以下のセットの値段を、同じ値は 1 回だけ、小さい順に 1 行に出力する。
そのようなセットがなければ - を出力する。
1 行に数を複数出力するときは空白 1 つで区切る。
この問題はリストから新しいリストを作る短い書き方(Python の内包表記、ほかの言語の map・filter など)の練習だが、採点は出力だけを見るので、ループで解いてもよい。

入力
1 行目に n、m、B、T、d が空白 1 つで区切られて与えられる。
2 行目にノートの値段 n 個、3 行目にペンの値段 m 個が空白 1 つで区切られて与えられる。
1 ≤ n, m ≤ 60、値段はどれも 1 以上 100,000 以下、1 ≤ B ≤ 200,000、1 ≤ d < T ≤ 100,000。
数はすべて整数である
出力
1 行目に値引き後のノートの値段 n 個、2 行目に値段が B 以下のセットの数、続けてセットごとに 1 行ずつノートの番号・ペンの番号・セットの値段、最後の行に異なるセットの値段(なければ -)。
数はコンマなしの数字だけで出力
例 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()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

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

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

Python の内包表記は、リストから新しいリストを 1 行で作ります。
値を変える [p - d if p >= T else p for p in notes]、二重に回して絞り込む [... for i ... for j ... if ...]、同じ値を 1 つにまとめる集合内包表記 {t for ...} が、この問題の 3 行です。
ほかの言語は同じことを別の道具でします。
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 と Craykon にはこういう書き方がないので、ループで空の配列・リストに 1 つずつ入れ(Craykon 追加)、並べ替えてから(C qsort、Craykon 並べ替え)すぐ前と同じ値を飛ばします。
落とし穴 1:Python の内包表記では if の位置で意味が変わります。
for の前の a if 条件 else b は値を選ぶ式なので要素はすべて残り、for の後ろの if は絞り込みの条件なので要素が減ります。
例で [p - d for p in notes if p >= T] と書くと 600 のノートが消えて、1 行目が 1000 800 になります。
落とし穴 2:for を 2 つ書くと、左が外側のループです。
順番を入れ替えるとペンが外側になり、例のセットは 2 2 1100 より先に 3 1 1100 が出ます。
C# LINQ の 2 つの from も同じ順番です。
落とし穴 3:波かっこ {...} は集合、角かっこ [...] はリストです。
角かっこで書くと同じ値がそのまま残り、例の最後の行が 900 1100 1100 1300 1300 になります。
落とし穴 4:JS・TS の sort() は、比較関数を渡さないと数を文字列に変えて辞書順で比べます。
例の最後の行が 1100 1300 900 になるので、sort((x, y) => x - y) と書きます。
Python の sorted、Java の sorted、C# の OrderBy、Craykon の 並べ替え は数を大きさで比べます。

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]);        }    }    コンソール.書く(고른것);}