{ }codelper
查询式处理(LINQ、流)集合P0385

整理寻宝时挖过的格子

在寻宝游戏里,n 个孩子在沙坑里挖了格子。
沙坑的格子用 (行, 列) 表示,宝藏埋在 (a, b) 这一格。
对每个孩子,按挖的顺序给出他挖过的格子。
有的孩子把同一格挖了好几次,也有的孩子一格都没挖。
格子 (r, c) 和宝藏之间的距离是 |r - a| + |c - b|。
对每个孩子输出一行:名字、这个孩子挖过的不同格子的数量、这个孩子挖过的格子里离宝藏最近的那一格的距离,按输入里出现的顺序输出。
一格都没挖的孩子,在距离的位置写 -。
下一行输出所有孩子挖过的格子合在一起有多少个不同的格子。
最后一行按输入里出现的顺序输出细心的孩子的名字。
细心的孩子是指至少挖了一格、并且挖过的格子与宝藏的距离都不超过 d 的孩子。
不要用循环一个一个地数,试着用查询式处理来解:把去重、取最小值、问是否全部满足、把列表接起来这些操作连在一起。

输入
第一行给出 n、a、b、d,用一个空格隔开。
接下来 n 行,每行给出孩子的名字、挖过的格子数 k,然后是 k 对格子的行和列,都用一个空格隔开。
1 ≤ n ≤ 100,1 ≤ a, b ≤ 1000,0 ≤ d ≤ 2000,0 ≤ k ≤ 50,1 ≤ r, c ≤ 1000。
名字由 1~10 个英文小写字母组成,互不相同
输出
n + 2 行。
每个孩子输出 "名字 格子数 距离",用一个空格隔开。
下一行输出一个数。
最后一行输出细心的孩子的名字,用一个空格隔开;如果没有细心的孩子,只输出 -
样例 1 · stdin
4 3 3 2
ann 4 3 4 2 2 3 4 3 3
bo 2 1 1 5 5
cy 0
dee 3 2 2 2 2 2 2
stdout
ann 3 0
bo 2 4
cy 0 -
dee 1 2
5
ann dee
样例 2 · stdin
2 5 5 1
zed 2 1 11 11 1
yo 1 5 7
stdout
zed 2 10
yo 1 2
3
-
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

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

其他语言怎么写?查看各语言题解 · 6 种语言题解

这道题只接受语言里自带筛选、变换列表的工具的 Python、JavaScript、TypeScript、Java(流)、C#(LINQ)和 C++(C++20 std::ranges)。
C 和 Craykon 没有接受函数来筛选、变换的列表工具。
同样的事在各语言里名字不同。
去重是 Python set(cells),JS·TS new Set(...),Java distinct(),C# Distinct(),C++ std::set。
把每个孩子的列表接成一个,是 Python 的嵌套推导式,JS·TS·Java 的 flatMap,C# 的 SelectMany,C++ 的 std::views::join。
问是否全部满足是 all、every、allMatch、All、std::ranges::all_of,最小值是 min、Math.min(...)、mapToInt(...).min()、Min、std::ranges::min。
要点是用什么来判断“同一格”。
Python 的元组、Java 的 record、C# 的值元组 (int R, int C)、C++ 的 pair 都按值来判断。
JS·TS 的 Set 对对象和数组是按“是不是同一个对象”来判断的,所以解法把格子变成字符串键 r + "," + c 再放进去。
陷阱一:在 JS 里把格子按 [r, c] 数组原样放进 Set,相同的格子不会合并。
第一个例子变成 ann 4、dee 3,合计 9。
在 Java 里用没有 equals 的普通类、在 C# 里用 class 来做格子,得到的答案完全一样。
陷阱二:如果不加分隔符,用 ${r}${c} 来做键,(1, 11) 和 (11, 1) 都是 111。
第二个例子变成 zed 1 10 和 2。
陷阱三:C++ 的 std::unique 只删掉紧挨着的相同值。
不排序就用,第一个例子变成 ann 4,合计 7。
陷阱四:“是否全部满足”对空列表为真(Python all、JS every)。
如果不另外问有没有挖过格子,第一个例子的最后一行就变成 ann cy dee。
陷阱五:空列表的最小值各语言不同。
Python 的 min 因 ValueError 而停止,Java 的 getAsInt() 因 NoSuchElementException 而停止,C# 的 Min 因 InvalidOperationException 而停止。
JS 不带参数的 Math.min() 不报错而是 Infinity,所以第一个例子的第三行变成 cy 0 Infinity。

Pythonmain.py · 25 行
import sys lines = sys.stdin.read().split("\n")n, a, b, d = map(int, lines[0].split())kids = []for i in range(1, n + 1):    p = lines[i].split()    nums = [int(x) for x in p[2:]]    # 칸은 (행, 열) 튜플: 값이 같으면 같은 것으로 친다    kids.append((p[0], list(zip(nums[0::2], nums[1::2]))))  def dist(cell):    return abs(cell[0] - a) + abs(cell[1] - b)  for name, cells in kids:    closest = min(map(dist, cells)) if cells else "-"  # 빈 것의 min은 ValueError    print(name, len(set(cells)), closest) print(len({cell for _, cells in kids for cell in cells})) # all은 빈 것에 참이므로 판 칸이 있는지를 따로 묻는다careful = [name for name, cells in kids if cells and all(dist(cell) <= d for cell in cells)]print(" ".join(careful) if careful else "-")
JavaScriptmain.js · 29 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const [n, a, b, d] = lines[0].trim().split(/\s+/).map(Number);const kids = [];for (let i = 1; i <= n; i++) {  const p = lines[i].trim().split(/\s+/);  const nums = p.slice(2).map(Number);  const cells = [];  for (let j = 0; j < nums.length; j += 2) cells.push({ r: nums[j], c: nums[j + 1] });  kids.push({ name: p[0], cells });} const dist = (cell) => Math.abs(cell.r - a) + Math.abs(cell.c - b);// Set은 객체·배열을 값이 아니라 '같은 객체인가'로 가리므로 문자열 열쇠로 바꿔 넣는다const key = (cell) => cell.r + "," + cell.c; const out = kids.map((kid) => {  const distinct = new Set(kid.cells.map(key)).size;  const closest = kid.cells.length ? Math.min(...kid.cells.map(dist)) : "-"; // 빈 것의 Math.min은 Infinity  return `${kid.name} ${distinct} ${closest}`;}); out.push(String(new Set(kids.flatMap((kid) => kid.cells.map(key))).size)); // every는 빈 배열에 참이므로 판 칸이 있는지를 따로 묻는다const careful = kids  .filter((kid) => kid.cells.length > 0 && kid.cells.every((cell) => dist(cell) <= d))  .map((kid) => kid.name);out.push(careful.length ? careful.join(" ") : "-");console.log(out.join("\n"));
TypeScriptmain.ts · 40 行
import { readFileSync } from "node:fs"; interface Cell {  r: number;  c: number;}interface Kid {  name: string;  cells: Cell[];} const lines: string[] = readFileSync(0, "utf8").split("\n");const [n, a, b, d] = lines[0].trim().split(/\s+/).map(Number);const kids: Kid[] = [];for (let i = 1; i <= n; i++) {  const p = lines[i].trim().split(/\s+/);  const nums = p.slice(2).map(Number);  const cells: Cell[] = [];  for (let j = 0; j < nums.length; j += 2) cells.push({ r: nums[j], c: nums[j + 1] });  kids.push({ name: p[0], cells });} const dist = (cell: Cell): number => Math.abs(cell.r - a) + Math.abs(cell.c - b);// Set은 객체·배열을 값이 아니라 '같은 객체인가'로 가리므로 문자열 열쇠로 바꿔 넣는다const key = (cell: Cell): string => cell.r + "," + cell.c; const out: string[] = kids.map((kid) => {  const distinct = new Set(kid.cells.map(key)).size;  const closest = kid.cells.length ? String(Math.min(...kid.cells.map(dist))) : "-"; // 빈 것의 Math.min은 Infinity  return `${kid.name} ${distinct} ${closest}`;}); out.push(String(new Set(kids.flatMap((kid) => kid.cells.map(key))).size)); // every는 빈 배열에 참이므로 판 칸이 있는지를 따로 묻는다const careful: string[] = kids  .filter((kid) => kid.cells.length > 0 && kid.cells.every((cell) => dist(cell) <= d))  .map((kid) => kid.name);out.push(careful.length ? careful.join(" ") : "-");console.log(out.join("\n"));
JavaMain.java · 46 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.List;import java.util.OptionalInt;import java.util.stream.Collectors; public class Main {    // record는 필드 값으로 equals·hashCode를 만들어 준다: distinct()가 값으로 가린다    record Cell(int r, int c) {}    record Kid(String name, List<Cell> cells) {}     public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        String[] first = in.readLine().trim().split(" ");        int n = Integer.parseInt(first[0]), a = Integer.parseInt(first[1]);        int b = Integer.parseInt(first[2]), d = Integer.parseInt(first[3]);        List<Kid> kids = new ArrayList<>();        for (int i = 0; i < n; i++) {            String[] p = in.readLine().trim().split(" ");            List<Cell> cells = new ArrayList<>();            for (int j = 2; j < p.length; j += 2) cells.add(new Cell(Integer.parseInt(p[j]), Integer.parseInt(p[j + 1])));            kids.add(new Kid(p[0], cells));        }         StringBuilder out = new StringBuilder();        for (Kid kid : kids) {            long distinct = kid.cells().stream().distinct().count();            // 빈 스트림의 min은 빈 OptionalInt다            OptionalInt closest = kid.cells().stream().mapToInt(c -> Math.abs(c.r() - a) + Math.abs(c.c() - b)).min();            out.append(kid.name()).append(' ').append(distinct).append(' ')               .append(closest.isPresent() ? String.valueOf(closest.getAsInt()) : "-").append('\n');        }         out.append(kids.stream().flatMap(kid -> kid.cells().stream()).distinct().count()).append('\n');         // allMatch는 빈 스트림에 참이므로 판 칸이 있는지를 따로 묻는다        String careful = kids.stream()                .filter(kid -> !kid.cells().isEmpty()                        && kid.cells().stream().allMatch(c -> Math.abs(c.r() - a) + Math.abs(c.c() - b) <= d))                .map(Kid::name)                .collect(Collectors.joining(" "));        out.append(careful.isEmpty() ? "-" : careful).append('\n');        System.out.print(out);    }}
C#Main.cs · 43 行
using System;using System.Collections.Generic;using System.Linq;using System.Text; class Program{    static void Main()    {        var lines = Console.In.ReadToEnd().Split('\n');        var first = lines[0].Trim().Split(' ').Select(int.Parse).ToArray();        int n = first[0], a = first[1], b = first[2], d = first[3];        // 칸은 값 튜플 (R, C): 값이 같으면 같은 것으로 친다        var kids = new List<(string Name, List<(int R, int C)> Cells)>();        for (int i = 1; i <= n; i++)        {            var p = lines[i].Trim().Split(' ');            var nums = p.Skip(2).Select(int.Parse).ToArray();            var cells = Enumerable.Range(0, nums.Length / 2).Select(j => (R: nums[2 * j], C: nums[2 * j + 1])).ToList();            kids.Add((p[0], cells));        }         Func<(int R, int C), int> dist = cell => Math.Abs(cell.R - a) + Math.Abs(cell.C - b);         var sb = new StringBuilder();        foreach (var kid in kids)        {            int distinct = kid.Cells.Distinct().Count();            // 빈 것의 Min()은 InvalidOperationException            string closest = kid.Cells.Any() ? kid.Cells.Min(dist).ToString() : "-";            sb.Append($"{kid.Name} {distinct} {closest}\n");        }         sb.Append(kids.SelectMany(kid => kid.Cells).Distinct().Count()).Append('\n');         // All은 빈 것에 참이므로 판 칸이 있는지를 따로 묻는다        var careful = kids.Where(kid => kid.Cells.Any() && kid.Cells.All(cell => dist(cell) <= d))                          .Select(kid => kid.Name)                          .ToList();        sb.Append(careful.Count > 0 ? string.Join(" ", careful) : "-").Append('\n');        Console.Write(sb);    }}
C++main.cpp · 55 行
#include <algorithm>#include <cstdlib>#include <iostream>#include <ranges>#include <set>#include <string>#include <utility>#include <vector> using Cell = std::pair<int, int>; // (행, 열): pair는 값으로 견준다 struct Kid {    std::string name;    std::vector<Cell> cells;}; int main() {    int n, a, b, d;    std::cin >> n >> a >> b >> d;    std::vector<Kid> kids(n);    for (Kid& kid : kids) {        int k;        std::cin >> kid.name >> k;        kid.cells.resize(k);        for (Cell& cell : kid.cells) std::cin >> cell.first >> cell.second;    }     auto dist = [&](const Cell& cell) { return std::abs(cell.first - a) + std::abs(cell.second - b); };     for (const Kid& kid : kids) {        // set에 넣으면 같은 칸은 하나만 남는다        std::set<Cell> distinct(kid.cells.begin(), kid.cells.end());        std::cout << kid.name << ' ' << distinct.size() << ' ';        // 빈 범위에는 ranges::min을 쓸 수 없다        if (kid.cells.empty()) std::cout << "-\n";        else std::cout << std::ranges::min(kid.cells | std::views::transform(dist)) << '\n';    }     // 아이마다의 칸 목록을 하나로 이어 붙인다    std::set<Cell> all;    for (const Cell& cell : kids | std::views::transform(&Kid::cells) | std::views::join) all.insert(cell);    std::cout << all.size() << '\n';     // all_of는 빈 범위에 참이므로 판 칸이 있는지를 따로 묻는다    auto careful = kids | std::views::filter([&](const Kid& kid) {                       return !kid.cells.empty()                              && std::ranges::all_of(kid.cells, [&](const Cell& cell) { return dist(cell) <= d; });                   });    bool any = false;    for (const Kid& kid : careful) {        std::cout << (any ? " " : "") << kid.name;        any = true;    }    std::cout << (any ? "\n" : "-\n");}