{ }codelper
最大值/最小值二维列表比较P0334

选一家谁都不会太失望的餐厅

根据 n 位朋友给 m 家餐厅打的评分表来选聚会地点。
表中第 r 行第 j 个值是 r 号朋友给 j 号餐厅打的评分。
评分是整数,没去过、没有打分的格子里写着 x。
对每家餐厅,求出它收到的评分中最低的值(最低)和最高的值(最高)。
x 不算在内。
一个评分都没有收到的餐厅不选。
选“最低”值最大的那家餐厅。
“最低”相同的餐厅有多家时,选其中“最高”值最大的,仍相同则选编号最小的。
然后数出最喜欢所选餐厅的朋友人数。
给所选餐厅打过分,并且这个评分等于自己打出的所有评分中最高值的朋友算在内(即使把最高值打给了多家餐厅也算)。
第一行给出 n 和 m,用一个空格隔开。
接下来 n 行,每行给出 m 个值,用一个空格隔开。
第一行输出所选餐厅的编号、最低、最高,用一个空格隔开,第二行输出最喜欢这家餐厅的朋友人数。
如果所有餐厅都没有收到任何评分,只输出一行 0。

输入
1 ≤ n ≤ 200。
1 ≤ m ≤ 200。
每个值是 x 或 −100 到 100 的整数
输出
第一行是餐厅编号、最低、最高。
第二行是朋友人数。
没有可选的餐厅时输出一行 0
样例 1 · stdin
3 4
5 3 x 4
2 3 x 4
x 3 x -1
stdout
2 3 3
1
样例 2 · stdin
2 2
x x
x x
stdout
0
main.py
输入(stdin)

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

$ 就绪

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

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

最小值和最大值,Python 用 min(col)・max(col),JS・TS 用 Math.min(...col)・Math.max(...col),C# 用 col.Min()・col.Max(),C++ 用 std::minmax_element 一次求出;C、Java、Craykon 在循环里像 数学.较小值(lo, v) 这样一个一个比较。
陷阱 ① 一个值都没有时,各语言表现不同。
Python 的 min([]) 会以 ValueError 停止,而 JS 的 Math.min() 返回 Infinity,Math.max() 返回 -Infinity。
所以不跳过没有评分的餐厅的话,在 JS 中最低为 Infinity 的那家餐厅会被选中(第一个样例会输出 3 Infinity -Infinity)。
要先确认收集到的列表是不是空的。
② 用循环求时不能从 0 开始。
评分只有 −5 和 −3 时最高会变成 0,只有 5 和 3 时最低会变成 0。
要从 C 的 INT_MAX・INT_MIN、Java 的 Integer.MAX_VALUE・Integer.MIN_VALUE 开始,或者像 Craykon 的解法那样从遇到的第一个评分开始。
③ 读入的值不转成数字就比较的话是字符串顺序,"9" 比 "10" 大。
④ 最低相同时用最高来决定的比较,Python 的元组 (lo, hi) > (best_lo, best_hi) 和 C++ std::pair 的 > 会从前面的值开始依次比较。
C# 的元组没有 >,要写 key.CompareTo(bestKey) > 0。
在 JS・TS 中写 [lo, hi] > [bestLo, bestHi],数组会变成字符串 "9,9"、"10,10" 再比较,所以是错的。
要拆开写成 lo > bestLo || (lo === bestLo && hi > bestHi)。
只比较最低而漏掉最高也是错的。
⑤ 从编号小的餐厅开始看,用 > 替换,相同时留下的就是编号小的餐厅。
写成 >= 会换成后面的餐厅。
⑥ 求朋友的最高评分时也要去掉 x。
在 Python 中对混有 None 的列表用 max(row) 是 TypeError。
C# 的 int?[] 用 row.Max() 会去掉 null 再求。
Craykon 把没有值的格子记为 空,用 v == 空 过滤(把空传给 数学.较大值 会停止)。
没有给所选餐厅打分的朋友不算在内。

Pythonmain.py · 32 行
import sys  def main():    data = sys.stdin.read().split()    n, m = int(data[0]), int(data[1])    # 별점이 없는 칸은 None으로 둔다    grid = [[None if tok == "x" else int(tok) for tok in data[2 + r * m:2 + (r + 1) * m]] for r in range(n)]     best = 0  # 고른 식당 번호(0이면 아직 없음)    best_key = None    for j in range(m):        col = [row[j] for row in grid if row[j] is not None]        if not col:  # min([])은 ValueError다: 별점이 없는 식당은 건너뛴다            continue        key = (min(col), max(col))  # 튜플은 앞의 값부터 차례로 견준다        if best == 0 or key > best_key:  # >라서 같으면 번호가 작은 식당이 남는다            best, best_key = j + 1, key    if best == 0:        print(0)        return     fans = 0    for row in grid:        mine = row[best - 1]        if mine is not None and mine == max(v for v in row if v is not None):            fans += 1    print(best, best_key[0], best_key[1])    print(fans)  main()
JavaScriptmain.js · 35 行
const tokens = require("fs").readFileSync(0, "utf8").split(/\s+/).filter((s) => s !== "");const n = Number(tokens[0]);const m = Number(tokens[1]);// 별점이 없는 칸은 null로 둔다. 문자열인 채로 견주면 "9" > "10"이라 수로 바꾼다const grid = [];for (let r = 0; r < n; r++) {  grid.push(tokens.slice(2 + r * m, 2 + (r + 1) * m).map((t) => (t === "x" ? null : Number(t))));} let best = 0; // 고른 식당 번호(0이면 아직 없음)let bestLo = 0;let bestHi = 0;for (let j = 0; j < m; j++) {  const col = grid.map((row) => row[j]).filter((v) => v !== null);  if (col.length === 0) continue; // Math.min()은 Infinity다: 별점이 없는 식당은 건너뛴다  const lo = Math.min(...col);  const hi = Math.max(...col);  // 배열끼리 >로 견주면 문자열로 바뀌므로 값을 하나씩 견준다  if (best === 0 || lo > bestLo || (lo === bestLo && hi > bestHi)) {    best = j + 1;    bestLo = lo;    bestHi = hi;  }} if (best === 0) {  console.log(0);} else {  let fans = 0;  for (const row of grid) {    const mine = row[best - 1];    if (mine !== null && mine === Math.max(...row.filter((v) => v !== null))) fans++;  }  console.log(best + " " + bestLo + " " + bestHi + "\n" + fans);}
TypeScriptmain.ts · 38 行
import { readFileSync } from "node:fs"; const tokens: string[] = readFileSync(0, "utf8").split(/\s+/).filter((s) => s !== "");const n: number = Number(tokens[0]);const m: number = Number(tokens[1]);// 별점이 없는 칸은 null로 둔다. 문자열인 채로 견주면 "9" > "10"이라 수로 바꾼다const grid: (number | null)[][] = [];for (let r = 0; r < n; r++) {  grid.push(tokens.slice(2 + r * m, 2 + (r + 1) * m).map((t) => (t === "x" ? null : Number(t))));}const rated = (v: number | null): v is number => v !== null; let best = 0; // 고른 식당 번호(0이면 아직 없음)let bestLo = 0;let bestHi = 0;for (let j = 0; j < m; j++) {  const col: number[] = grid.map((row) => row[j]).filter(rated);  if (col.length === 0) continue; // Math.min()은 Infinity다: 별점이 없는 식당은 건너뛴다  const lo: number = Math.min(...col);  const hi: number = Math.max(...col);  // 배열끼리 >로 견주면 문자열로 바뀌므로 값을 하나씩 견준다  if (best === 0 || lo > bestLo || (lo === bestLo && hi > bestHi)) {    best = j + 1;    bestLo = lo;    bestHi = hi;  }} if (best === 0) {  console.log(0);} else {  let fans = 0;  for (const row of grid) {    const mine = row[best - 1];    if (mine !== null && mine === Math.max(...row.filter(rated))) fans++;  }  console.log(best + " " + bestLo + " " + bestHi + "\n" + fans);}
JavaMain.java · 54 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.StringTokenizer; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(in.readLine());        int n = Integer.parseInt(st.nextToken()), m = Integer.parseInt(st.nextToken());        Integer[][] grid = new Integer[n][m]; // 별점이 없는 칸은 null        for (int r = 0; r < n; r++) {            st = new StringTokenizer(in.readLine());            for (int j = 0; j < m; j++) {                String tok = st.nextToken();                grid[r][j] = tok.equals("x") ? null : Integer.valueOf(tok);            }        }         int best = 0, bestLo = 0, bestHi = 0; // best가 0이면 아직 고른 식당이 없다        for (int j = 0; j < m; j++) {            // 0이 아니라 가장 큰 값·가장 작은 값에서 시작해야 음수 별점도 맞게 나온다            int lo = Integer.MAX_VALUE, hi = Integer.MIN_VALUE, count = 0;            for (int r = 0; r < n; r++) {                if (grid[r][j] == null) continue;                lo = Math.min(lo, grid[r][j]);                hi = Math.max(hi, grid[r][j]);                count++;            }            if (count == 0) continue; // 별점이 없는 식당은 건너뛴다            if (best == 0 || lo > bestLo || (lo == bestLo && hi > bestHi)) {                best = j + 1;                bestLo = lo;                bestHi = hi;            }        }        if (best == 0) {            System.out.println(0);            return;        }         int fans = 0;        for (int r = 0; r < n; r++) {            if (grid[r][best - 1] == null) continue;            int top = Integer.MIN_VALUE;            for (int j = 0; j < m; j++) {                if (grid[r][j] != null) top = Math.max(top, grid[r][j]);            }            if (grid[r][best - 1] == top) fans++;        }        System.out.println(best + " " + bestLo + " " + bestHi);        System.out.println(fans);    }}
C#Main.cs · 47 行
using System;using System.Linq; class Program{    static void Main()    {        string[] head = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        int n = int.Parse(head[0]), m = int.Parse(head[1]);        var grid = new int?[n][]; // 별점이 없는 칸은 null        for (int r = 0; r < n; r++)        {            grid[r] = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries)                .Select(tok => tok == "x" ? (int?)null : int.Parse(tok)).ToArray();        }         int best = 0; // 고른 식당 번호(0이면 아직 없음)        (int lo, int hi) bestKey = (0, 0);        for (int j = 0; j < m; j++)        {            int[] col = grid.Where(row => row[j] != null).Select(row => row[j].Value).ToArray();            if (col.Length == 0) continue; // 빈 배열의 Min()은 예외다: 별점이 없는 식당은 건너뛴다            var key = (col.Min(), col.Max());            // 튜플에는 > 연산자가 없어서 CompareTo로 앞의 값부터 견준다            if (best == 0 || key.CompareTo(bestKey) > 0)            {                best = j + 1;                bestKey = key;            }        }        if (best == 0)        {            Console.WriteLine(0);            return;        }         int fans = 0;        foreach (int?[] row in grid)        {            int? mine = row[best - 1];            // int?의 Max()는 null을 빼고 구한다            if (mine != null && mine == row.Max()) fans++;        }        Console.WriteLine($"{best} {bestKey.lo} {bestKey.hi}");        Console.WriteLine(fans);    }}
C++main.cpp · 53 行
#include <algorithm>#include <iostream>#include <optional>#include <string>#include <utility>#include <vector> int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    int n, m;    std::cin >> n >> m;    // 별점이 없는 칸은 std::nullopt    std::vector<std::vector<std::optional<int>>> grid(n, std::vector<std::optional<int>>(m));    for (auto& row : grid) {        for (auto& cell : row) {            std::string tok;            std::cin >> tok;            if (tok != "x") cell = std::stoi(tok);        }    }     int best = 0; // 고른 식당 번호(0이면 아직 없음)    std::pair<int, int> bestKey;    for (int j = 0; j < m; j++) {        std::vector<int> col;        for (const auto& row : grid) {            if (row[j]) col.push_back(*row[j]);        }        if (col.empty()) continue; // 별점이 없는 식당은 건너뛴다        auto [lo, hi] = std::minmax_element(col.begin(), col.end());        std::pair<int, int> key(*lo, *hi); // pair는 앞의 값부터 차례로 견준다        if (best == 0 || key > bestKey) {            best = j + 1;            bestKey = key;        }    }    if (best == 0) {        std::cout << 0 << '\n';        return 0;    }     int fans = 0;    for (const auto& row : grid) {        if (!row[best - 1]) continue;        int top = *row[best - 1];        for (const auto& cell : row) {            if (cell) top = std::max(top, *cell);        }        if (*row[best - 1] == top) fans++;    }    std::cout << best << ' ' << bestKey.first << ' ' << bestKey.second << '\n' << fans << '\n';}
Cmain.c · 54 行
#include <limits.h>#include <stdio.h>#include <stdlib.h> #define MAXN 200#define NONE INT_MIN /* 별점이 없는 칸 */ static int grid[MAXN][MAXN]; int main(void) {    int n, m;    char tok[16];    scanf("%d %d", &n, &m);    for (int r = 0; r < n; r++) {        for (int j = 0; j < m; j++) {            scanf("%15s", tok);            grid[r][j] = tok[0] == 'x' ? NONE : atoi(tok);        }    }     int best = 0, bestLo = 0, bestHi = 0; /* best가 0이면 아직 고른 식당이 없다 */    for (int j = 0; j < m; j++) {        /* 0이 아니라 가장 큰 값·가장 작은 값에서 시작해야 음수 별점도 맞게 나온다 */        int lo = INT_MAX, hi = INT_MIN, count = 0;        for (int r = 0; r < n; r++) {            if (grid[r][j] == NONE) continue;            if (grid[r][j] < lo) lo = grid[r][j];            if (grid[r][j] > hi) hi = grid[r][j];            count++;        }        if (count == 0) continue; /* 별점이 없는 식당은 건너뛴다 */        if (best == 0 || lo > bestLo || (lo == bestLo && hi > bestHi)) {            best = j + 1;            bestLo = lo;            bestHi = hi;        }    }    if (best == 0) {        puts("0");        return 0;    }     int fans = 0;    for (int r = 0; r < n; r++) {        if (grid[r][best - 1] == NONE) continue;        int top = INT_MIN;        for (int j = 0; j < m; j++) {            if (grid[r][j] != NONE && grid[r][j] > top) top = grid[r][j];        }        if (grid[r][best - 1] == top) fans++;    }    printf("%d %d %d\n%d\n", best, bestLo, bestHi, fans);    return 0;}
Craykonmain.cray · 95 行
head = 控制台.读数字();n = head[0];m = head[1];// 별점이 없는 칸은 빈값으로 둔다grid = [];循环 (r = 0; r < n; r++){    row = [];    遍历 (tok 在 控制台.读行().去空白().分割(" "))    {        如果 (tok == "x")        {            row.添加(空);        }        否则        {            row.添加(tok.转数字());        }    }    grid.添加(row);} // best가 0이면 아직 고른 식당이 없다best = 0;bestLo = 0;bestHi = 0;循环 (j = 0; j < m; j++){    // 0이 아니라 처음 만난 별점에서 시작해야 음수 별점도 맞게 나온다    count = 0;    lo = 0;    hi = 0;    循环 (r = 0; r < n; r++)    {        v = grid[r][j];        如果 (v == 空)        {            继续;        }        如果 (count == 0)        {            lo = v;            hi = v;        }        否则        {            lo = 数学.较小值(lo, v);            hi = 数学.较大值(hi, v);        }        count++;    }    // 별점이 없는 식당은 건너뛴다    如果 (count == 0)    {        继续;    }    如果 (best == 0 || lo > bestLo || lo == bestLo && hi > bestHi)    {        best = j + 1;        bestLo = lo;        bestHi = hi;    }} 如果 (best == 0){    控制台.输出(0);}否则{    fans = 0;    遍历 (row 在 grid)    {        mine = row[best - 1];        如果 (mine == 空)        {            继续;        }        // 이 친구가 준 가장 높은 별점: mine에서 시작하면 된다        top = mine;        遍历 (v 在 row)        {            如果 (v != 空)            {                top = 数学.较大值(top, v);            }        }        如果 (mine == top)        {            fans++;        }    }    控制台.输出(best + " " + bestLo + " " + bestHi);    控制台.输出(fans);}