{ }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()/标准输入传入(运行中不会等待输入)。

$ 就绪

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

其他语言怎么写?查看各语言题解 · 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 和 Craykon 没有这种写法,就用循环往空数组、空列表里一个一个放(Craykon 添加),排序之后(C qsort,Craykon 排序)跳过和前一个相同的值。
陷阱一:在 Python 推导式里,if 的位置决定它的意思。
for 前面的 a if 条件 else b 是选择值的表达式,所有元素都留下;for 后面的 if 是筛选条件,元素会变少。
在示例中写成 [p - d for p in notes if p >= T],600 的笔记本就不见了,第一行变成 1000 800。
陷阱二:写两个 for 时,左边的是外层循环。
调换顺序后笔成了外层,示例中 3 1 1100 会排在 2 2 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、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]);        }    }    控制台.输出(고른것);}