{ }codelper
最大・最小2次元リスト比較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);}