{ }codelper
条件つき並べ替えP0376

誕生日が早く来る友だちから

今日は M 月 D 日である。
友だち n 人の名前と誕生日(月と日)が与えられる。
今日から数えて誕生日が早く来る友だちから順に、名前を 1 行に 1 つずつ出力せよ。
誕生日が今日の友だちは 0 日後なので、いちばん先に来る。
誕生日の日付が今日より前の友だちは、今年の誕生日がもう過ぎているので、来年の誕生日を待つ。
このような友だちは、誕生日が今日か今日より後の友だちの次に、1 月から日付の順に来る。
誕生日が同じ日の友だちどうしは、名前の辞書順(アルファベット順。
一方の名前がもう一方の先頭部分と同じなら短いほうが先)に並べる。

入力
1 行目に M と D が空白 1 つで区切られて与えられる。
2 行目に n が与えられる。
続く n 行に、友だちごとに名前、誕生日の月、誕生日の日が空白 1 つずつで区切られて与えられる。
1 ≤ n ≤ 100。
名前は英小文字 1 〜 10 文字で、すべて異なる。
今日と誕生日はどれも暦にある日付で、2 月 29 日は与えられない
出力
n 行。
誕生日が早く来る友だちから順に、名前を 1 行に 1 つずつ
例 1 · stdin
10 5
6
mina 10 5
joon 3 14
sora 12 25
hana 9 30
dan 10 20
bora 12 25
stdout
mina
dan
bora
sora
joon
hana
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

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

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

並べ替えの基準がいくつもあるときの書き方は言語ごとに違います。
Python は sorted(friends, key=key) のキーにタプル (過ぎたか, 月, 日, 名前) を渡すと、前の要素から順に比べます。
日付の比較も (m, d) < (M, D) のようにタプルで一度にできます。
C++ も std::tie(…) < std::tie(…) で同じことができますが、std::sort に渡す関数は「a は b より前か」を bool で返します。
JS・TS の sort、C の qsort、C# の List.Sort、Craykon の リスト.並べ替え(関数) に渡す関数は逆に数を返します。
前が先なら負、同じなら 0、後ろが先なら正です。
JS は a.passed - b.passed || a.month - b.month || … のように || でつなぎ、0(同じ)なら次の基準に進みます。
Java は Comparator.comparingInt(Friend::passed).thenComparingInt(Friend::month)… で基準をつなげます。
名前は C では strcmp、C# では string.CompareOrdinal、JS と Craykon では < で比べます。
落とし穴 1:月と日だけで並べ替えると、今年もう過ぎた誕生日が先頭に来ます。
例の 1 行目が joon(3 月 14 日)になります。
落とし穴 2:今日が誕生日の友だちは、まだ過ぎていない側です。
過ぎたかどうかを < ではなく <= で確かめると、例の mina がいちばん後ろに行きます。
落とし穴 3:入力から読んだ月と日は文字列です。
数に変えずに比べると "10" が "9" より、"12" が "3" より前になり、例が mina、bora、sora、joon、hana、dan の順になります。
Python は int()、JS は Number()、Craykon は 数に() で変えます。
落とし穴 4:JS の sort に C++ のように true/false を返す関数を渡すと、false が 0、つまり「同じ」と読まれます。
負の数が一度も出ないので、Node では例が並べ替えられず、入力の順のまま出ます。

Pythonmain.py · 20 行
import sys lines = sys.stdin.read().split("\n")M, D = map(int, lines[0].split())n = int(lines[1])friends = []for line in lines[2:2 + n]:    name, m, d = line.split()    friends.append((name, int(m), int(d)))  # 정렬 열쇠: (올해 생일이 이미 지났나, 월, 일, 이름). 튜플은 앞 칸부터 차례로 견준다.# False가 True보다 앞이라 아직 지나지 않은 생일이 먼저 온다. 오늘이 생일이면 지나지 않은 것이다.def key(friend):    name, m, d = friend    return ((m, d) < (M, D), m, d, name)  for name, m, d in sorted(friends, key=key):    print(name)
JavaScriptmain.js · 21 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const [M, D] = lines[0].trim().split(" ").map(Number);const n = Number(lines[1]);const friends = [];for (let i = 2; i < 2 + n; i++) {  const [name, m, d] = lines[i].trim().split(" ");  const month = Number(m);  const day = Number(d);  // 올해 생일이 이미 지났으면 1, 아니면 0. 오늘이 생일이면 지나지 않은 것이다.  const passed = month < M || (month === M && day < D) ? 1 : 0;  friends.push({ name, month, day, passed });}// 견주는 함수는 앞이 먼저면 음수, 뒤가 먼저면 양수를 돌려준다. 0이면 || 뒤의 다음 기준으로 넘어간다.friends.sort(  (a, b) =>    a.passed - b.passed ||    a.month - b.month ||    a.day - b.day ||    (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));console.log(friends.map((f) => f.name).join("\n"));
TypeScriptmain.ts · 30 行
import { readFileSync } from "node:fs"; interface Friend {  name: string;  month: number;  day: number;  passed: number; // 올해 생일이 이미 지났으면 1, 아니면 0} const lines: string[] = readFileSync(0, "utf8").split("\n");const [M, D]: number[] = lines[0].trim().split(" ").map(Number);const n: number = Number(lines[1]);const friends: Friend[] = [];for (let i = 2; i < 2 + n; i++) {  const [name, m, d]: string[] = lines[i].trim().split(" ");  const month = Number(m);  const day = Number(d);  // 오늘이 생일이면 지나지 않은 것이다.  const passed = month < M || (month === M && day < D) ? 1 : 0;  friends.push({ name, month, day, passed });}// 견주는 함수는 앞이 먼저면 음수, 뒤가 먼저면 양수를 돌려준다. 0이면 || 뒤의 다음 기준으로 넘어간다.friends.sort(  (a: Friend, b: Friend): number =>    a.passed - b.passed ||    a.month - b.month ||    a.day - b.day ||    (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));console.log(friends.map((f: Friend): string => f.name).join("\n"));
JavaMain.java · 38 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.Comparator;import java.util.List; public class Main {    // passed: 올해 생일이 이미 지났으면 1, 아니면 0    record Friend(String name, int month, int day, int passed) {}     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        String[] today = in.readLine().trim().split(" ");        int M = Integer.parseInt(today[0]);        int D = Integer.parseInt(today[1]);        int n = Integer.parseInt(in.readLine().trim());        List<Friend> friends = new ArrayList<>();        for (int i = 0; i < n; i++) {            String[] parts = in.readLine().trim().split(" ");            int month = Integer.parseInt(parts[1]);            int day = Integer.parseInt(parts[2]);            // 오늘이 생일이면 지나지 않은 것이다.            int passed = (month < M || (month == M && day < D)) ? 1 : 0;            friends.add(new Friend(parts[0], month, day, passed));        }        // 기준을 차례로 잇는다: 앞 기준이 같을 때만 다음 기준을 본다.        friends.sort(Comparator.comparingInt(Friend::passed)                .thenComparingInt(Friend::month)                .thenComparingInt(Friend::day)                .thenComparing(Friend::name));        StringBuilder out = new StringBuilder();        for (Friend f : friends) {            out.append(f.name()).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 39 行
using System;using System.Collections.Generic;using System.Text; class Program{    static void Main()    {        string[] today = Console.ReadLine().Trim().Split(' ');        int M = int.Parse(today[0]);        int D = int.Parse(today[1]);        int n = int.Parse(Console.ReadLine().Trim());        // passed: 올해 생일이 이미 지났으면 1, 아니면 0        var friends = new List<(string name, int month, int day, int passed)>();        for (int i = 0; i < n; i++)        {            string[] parts = Console.ReadLine().Trim().Split(' ');            int month = int.Parse(parts[1]);            int day = int.Parse(parts[2]);            // 오늘이 생일이면 지나지 않은 것이다.            int passed = (month < M || (month == M && day < D)) ? 1 : 0;            friends.Add((parts[0], month, day, passed));        }        // 견주는 함수는 앞이 먼저면 음수, 같으면 0, 뒤가 먼저면 양수를 돌려준다.        friends.Sort((a, b) =>        {            if (a.passed != b.passed) return a.passed.CompareTo(b.passed);            if (a.month != b.month) return a.month.CompareTo(b.month);            if (a.day != b.day) return a.day.CompareTo(b.day);            return string.CompareOrdinal(a.name, b.name); // 글자 코드 순서로 견준다        });        var sb = new StringBuilder();        foreach (var f in friends)        {            sb.Append(f.name).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 32 行
#include <algorithm>#include <iostream>#include <string>#include <tuple>#include <vector>using namespace std; struct Friend {    string name;    int month, day;    bool passed; // 올해 생일이 이미 지났는가}; int main() {    int M, D, n;    cin >> M >> D >> n;    vector<Friend> friends(n);    for (Friend &f : friends) {        cin >> f.name >> f.month >> f.day;        // pair는 앞 칸부터 견준다. 오늘이 생일이면 지나지 않은 것이다.        f.passed = make_pair(f.month, f.day) < make_pair(M, D);    }    // C++의 견주는 함수는 "a가 b보다 앞인가"를 bool로 돌려준다.    // tie로 묶으면 앞 칸부터 차례로 견준다(false가 true보다 앞).    sort(friends.begin(), friends.end(), [](const Friend &a, const Friend &b) {        return tie(a.passed, a.month, a.day, a.name) < tie(b.passed, b.month, b.day, b.name);    });    for (const Friend &f : friends) {        cout << f.name << '\n';    }    return 0;}
Cmain.c · 37 行
#include <stdio.h>#include <stdlib.h>#include <string.h> typedef struct {    char name[11];    int month, day;    int passed; /* 올해 생일이 이미 지났으면 1, 아니면 0 */} Friend; /* qsort가 부르는 견주는 함수: 앞이 먼저면 음수, 같으면 0, 뒤가 먼저면 양수 */static int compare_friends(const void *pa, const void *pb) {    const Friend *a = pa;    const Friend *b = pb;    if (a->passed != b->passed) return a->passed - b->passed;    if (a->month != b->month) return a->month - b->month;    if (a->day != b->day) return a->day - b->day;    return strcmp(a->name, b->name);} int main(void) {    int M, D, n;    static Friend friends[100];    scanf("%d %d", &M, &D);    scanf("%d", &n);    for (int i = 0; i < n; i++) {        Friend *f = &friends[i];        scanf("%10s %d %d", f->name, &f->month, &f->day);        /* 오늘이 생일이면 지나지 않은 것이다. */        f->passed = (f->month < M || (f->month == M && f->day < D)) ? 1 : 0;    }    qsort(friends, n, sizeof(Friend), compare_friends);    for (int i = 0; i < n; i++) {        printf("%s\n", friends[i].name);    }    return 0;}
Craykonmain.cray · 60 行
오늘 = コンソール.数読み();M = 오늘[0];D = 오늘[1];n = コンソール.行読み().数に();names = [];months = [];days = [];passed = []; // 올해 생일이 이미 지났으면 1, 아니면 0order = []; // 친구 번호를 담아 두고 이 목록을 정렬한다繰り返す (i = 0; i < n; i++){    parts = コンソール.行読み().空白除去().分ける(" ");    m = parts[1].数に();    d = parts[2].数に();    names.追加(parts[0]);    months.追加(m);    days.追加(d);    // 오늘이 생일이면 지나지 않은 것이다    もし (m < M || m == M && d < D)    {        passed.追加(1);    }    でなければ    {        passed.追加(0);    }    order.追加(i);} // 견주는 함수: 앞이 먼저면 음수, 같으면 0, 뒤가 먼저면 양수를 돌려준다먼저오는생일(a, b){    もし (passed[a] != passed[b])    {        返す passed[a] - passed[b];    }    もし (months[a] != months[b])    {        返す months[a] - months[b];    }    もし (days[a] != days[b])    {        返す days[a] - days[b];    }    もし (names[a] < names[b])    {        返す -1;    }    もし (names[a] > names[b])    {        返す 1;    }    返す 0;} order.並べ替え(먼저오는생일);それぞれ (i の中 order){    コンソール.書く(names[i]);}