为了两人一组的活动,学生们各写下一位想一起做的朋友。 第一行给出申请数 n,接下来 n 行按到达顺序给出申请 "a b",表示 a 选了 b。 同一个学生申请多次时,只保留最后一次申请。 学生的顺序是他第一次申请的顺序,重新申请也不会改变。 如果两个学生 x、y 在保留下来的申请中互相选了对方,他们就是互相的搭档。 学生不会选自己。 被选的朋友可能一次也没有申请过。 第一行输出互为搭档的对数。 接着每对输出一行 "x y",x 是两人中顺序靠前的学生。 各对按 x 的顺序输出。 没有搭档时只输出第一行的 0。
输入
1 ≤ n ≤ 5,000。 名字由 1~10 个小写英文字母组成,一次申请中的两个名字不同
输出
第一行为对数,接着每对一行(x y),按 x 的顺序
样例 1 · stdin
7
mina jun
jun sora
sora jun
dan mina
mina dan
eve kai
jun mina
stdout
1
mina dan
样例 2 · stdin
3
b a
a b
c a
stdout
1
b a
main.py
错误列表00F8 ↓ · ⇧F8 ↑
输入(stdin)
运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。
$ 就绪
请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。
其他语言怎么写?查看各语言题解 · 8 种语言题解
把最后一次申请放进字典(pick[a] = b),再按插入顺序遍历这个字典,检查 pick[b] == a 即可。 给已有的键重新赋值时,这个键仍留在第一次插入的位置(Python dict、JS/TS Map、Java LinkedHashMap、Craykon 字典的 键列表)。 只保留第一次申请的 setdefault、putIfAbsent、TryAdd 是错的。 顺序是陷阱。 Java HashMap 和 C++ unordered_map 顺序是乱的,C++ std::map 按名字的字典序遍历。 C# Dictionary 的遍历顺序文档并不保证,所以把第一次申请的名字另记在一个列表里,遍历那个列表。 被选的朋友可能没有申请过,这也是陷阱。 C# pick[b] 会抛出 KeyNotFoundException,所以用 TryGetValue;Python 用 pick.get(b);Craykon 先用 包含 询问(读取不存在的键会停止)。 C++ pick[b] 不报错,却会插入一个空值,所以用 find。 如果遍历时只检查是否互选,同一对会在 x 的顺序和 y 的顺序各出现一次,所以要给已经写过的搭档做标记。 C 没有字典,要按第一次申请的顺序记进数组,用 strcmp 从前往后找。
Pythonmain.py · 16 行
n = int(input())pick = {}for _ in range(n): a, b = input().split() pick[a] = b # 다시 내면 값만 바뀌고, 열쇠의 차례(처음 넣은 차례)는 그대로다pairs = []done = set()for a, b in pick.items(): # dict는 열쇠를 처음 넣은 차례대로 돈다 if a in done: continue if pick.get(b) == a: pairs.append(f"{a} {b}") done.add(b) # b 차례가 와도 같은 쌍을 다시 쓰지 않는다print(len(pairs))for p in pairs: print(p)
JavaScriptmain.js · 18 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);// Map은 열쇠를 처음 넣은 차례대로 돈다. set으로 값을 바꿔도 차례는 그대로다const pick = new Map();for (let i = 1; i <= n; i++) { const [a, b] = lines[i].trim().split(" "); pick.set(a, b);}const pairs = [];const done = new Set();for (const [a, b] of pick) { if (done.has(a)) continue; if (pick.get(b) === a) { pairs.push(`${a} ${b}`); done.add(b); }}console.log([String(pairs.length), ...pairs].join("\n"));
TypeScriptmain.ts · 19 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);// Map은 열쇠를 처음 넣은 차례대로 돈다. set으로 값을 바꿔도 차례는 그대로다const pick = new Map<string, string>();for (let i = 1; i <= n; i++) { const [a, b] = lines[i].trim().split(" "); pick.set(a, b);}const pairs: string[] = [];const done = new Set<string>();for (const [a, b] of pick) { if (done.has(a)) continue; if (pick.get(b) === a) { pairs.push(`${a} ${b}`); done.add(b); }}console.log([String(pairs.length), ...pairs].join("\n"));
JavaMain.java · 35 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.HashSet;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;import java.util.Set;public class Main { public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine().trim()); // HashMap은 차례가 뒤섞인다. LinkedHashMap은 처음 넣은 차례를 지키고, put으로 값을 바꿔도 차례는 그대로다 Map<String, String> pick = new LinkedHashMap<>(); for (int i = 0; i < n; i++) { String[] p = br.readLine().trim().split(" "); pick.put(p[0], p[1]); } List<String> pairs = new ArrayList<>(); Set<String> done = new HashSet<>(); for (Map.Entry<String, String> e : pick.entrySet()) { String a = e.getKey(), b = e.getValue(); if (done.contains(a)) continue; if (a.equals(pick.get(b))) { // b가 신청하지 않았으면 get은 null pairs.add(a + " " + b); done.add(b); } } StringBuilder out = new StringBuilder(); out.append(pairs.size()).append('\n'); for (String s : pairs) out.append(s).append('\n'); System.out.print(out); }}
C#Main.cs · 37 行
using System;using System.Collections.Generic;using System.Text;class Program{ static void Main() { int n = int.Parse(Console.ReadLine().Trim()); // Dictionary의 도는 차례는 문서가 약속하지 않으니, 처음 신청한 차례를 목록에 따로 적는다 var pick = new Dictionary<string, string>(); var order = new List<string>(); for (int i = 0; i < n; i++) { string[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries); if (!pick.ContainsKey(p[0])) order.Add(p[0]); pick[p[0]] = p[1]; } var pairs = new List<string>(); var done = new HashSet<string>(); foreach (string a in order) { string b = pick[a]; if (done.Contains(a)) continue; // pick[b]는 b가 신청하지 않았으면 KeyNotFoundException — TryGetValue로 묻는다 if (pick.TryGetValue(b, out string back) && back == a) { pairs.Add(a + " " + b); done.Add(b); } } var sb = new StringBuilder(); sb.Append(pairs.Count).Append('\n'); foreach (string s in pairs) sb.Append(s).Append('\n'); Console.Write(sb); }}
C++main.cpp · 33 行
#include <iostream>#include <string>#include <unordered_map>#include <unordered_set>#include <vector>int main() { std::ios::sync_with_stdio(false); int n; std::cin >> n; // std::map은 이름의 사전 순으로, unordered_map은 뒤섞인 차례로 돈다. 처음 신청한 차례는 따로 적는다 std::unordered_map<std::string, std::string> pick; std::vector<std::string> order; for (int i = 0; i < n; i++) { std::string a, b; std::cin >> a >> b; if (pick.find(a) == pick.end()) order.push_back(a); pick[a] = b; } std::vector<std::string> pairs; std::unordered_set<std::string> done; for (const std::string& a : order) { if (done.count(a)) continue; const std::string& b = pick[a]; auto it = pick.find(b); // pick[b]로 물으면 없는 b가 빈 값으로 생긴다 if (it != pick.end() && it->second == a) { pairs.push_back(a + " " + b); done.insert(b); } } std::cout << pairs.size() << "\n"; for (const std::string& s : pairs) std::cout << s << "\n";}
Cmain.c · 45 行
#include <stdio.h>#include <string.h>/* C에는 사전이 없어서, 처음 신청한 차례대로 이름과 고른 친구를 배열에 적고 앞에서부터 찾는다 */#define MAXN 5000static char name[MAXN][11];static char pick[MAXN][11];static int done[MAXN];static int kinds = 0;static int find(const char *s) { for (int i = 0; i < kinds; i++) if (strcmp(name[i], s) == 0) return i; return -1;}int main(void) { int n; char a[16], b[16]; scanf("%d", &n); for (int i = 0; i < n; i++) { scanf("%15s %15s", a, b); int k = find(a); if (k < 0) { k = kinds++; strcpy(name[k], a); } strcpy(pick[k], b); /* 다시 내면 고른 친구만 바뀐다 */ } int count = 0; static int first[MAXN], second[MAXN]; for (int i = 0; i < kinds; i++) { if (done[i]) continue; int j = find(pick[i]); if (j >= 0 && strcmp(pick[j], name[i]) == 0) { first[count] = i; second[count] = j; count++; done[j] = 1; } } printf("%d\n", count); for (int k = 0; k < count; k++) printf("%s %s\n", name[first[k]], name[second[k]]); return 0;}
Craykonmain.cray · 27 行
n = 控制台.读行().转数字();pick = 字典();循环 (i = 0; i < n; i++){ p = 控制台.读行().去空白().分割(" "); pick[p[0]] = p[1]; // 다시 내면 값만 바뀌고 열쇠의 차례는 그대로다}pairs = [];done = 字典();// 크레이콘 사전의 열쇠목록은 처음 넣은 차례다遍历 (a 在 pick.键列表){ b = pick[a]; 如果 (!done.包含(a) && pick.包含(b)) { 如果 (pick[b] == a) { pairs.添加(a + " " + b); done[b] = 真; } }}控制台.输出(pairs.数量);遍历 (s 在 pairs){ 控制台.输出(s);}