{ }codelper
辞書で数える数えるリストに含まれるかP0148

順位を書く投票の決選

候補者 c 人について投票用紙を v 枚集めた。
投票用紙には好きな順に候補者の名前が一人以上書かれていて、全員を書く必要はない。
一行目に c と v が、二行目に候補者の名前 c 個が空白一つずつで区切られて与えられる。
続く v 行に投票用紙が一枚ずつ与えられる。
最初はすべての候補者が残っていて、次の集計を回ごとに繰り返す。
1.
投票用紙ごとに、その用紙に書かれた名前のうち、まだ残っている候補者でいちばん前に書かれた候補者に一票を与える。
残っている候補者が一人も書かれていない用紙は使い切った用紙なので、この回には数えない。
2.
残っている候補者ごとに "名前:票数" を二行目の順に空白一つずつ空けて一行に出力する。
票がない候補者も "名前:0" と出力する。
3.
ある候補者の票数がこの回に数えた用紙の数の半分より多ければ(ちょうど半分は含まない)、または残っている候補者が一人だけなら、その候補者が当選である。
次の行に当選者の名前を出力して終える。
4.
そうでなければ票がいちばん少ない候補者を一人落として次の回に進む。
票がいちばん少ない候補者が複数いれば、その中で二行目でいちばん後ろに書かれた候補者を落とす。

入力
1 ≤ c ≤ 20、1 ≤ v ≤ 1000。
候補者の名前は英小文字・数字 1 ~ 10 文字で互いに異なる。
投票用紙一枚には互いに異なる候補者の名前が 1 ~ c 個、空白一つずつで区切られて書かれている
出力
回ごとに "名前:票数" を一行、最後の行に当選者の名前
例 1 · stdin
4 9
kim lee park choi
kim lee
kim
lee park
lee kim
park lee
park
choi park
choi
kim park
stdout
kim:3 lee:2 park:2 choi:2
kim:3 lee:2 park:3
kim:4 park:4
kim:4
kim
例 2 · stdin
3 3
a b c
b
b a
c
stdout
a:0 b:2 c:1
b
main.py
入力(stdin)

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

$ 準備完了

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

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

回ごとに辞書で票を数え、使い切っていない用紙の数も別に数え、名前が残っている候補者のリストに含まれるかを確かめます。
集計の辞書は、残っている候補者を全員 0 で先に入れておくのが要点です:Python {name: 0 for name in remaining}、JS・TS new Map(remaining.map(name => [name, 0]))、Java put(name, 0) の後に merge(name, 1, Integer::sum)、C# remaining.ToDictionary(name => name, name => 0)、C++ std::map の votes[name] = 0、Craykon votes[name] = 0;(辞書()、韓国語 사전())。
C は候補者番号ごとに枠を持つ配列 int votes[20] = {0} です。
落とし穴その一:Python の Counter() で受け取った票だけを数えると、0 票の候補者は辞書に入りません。
votes[name] はない名前に 0 を返すので行は正しく出ますが、min(votes.values()) が 0 を見落とし、0 票の候補者ではなく別の候補者を落としてしまいます。
リストに含まれるかは Python in、JS・TS includes、Java contains、C# Contains、C++ std::find(...) != end()、Craykon 含む(韓国語 있나)です。
落とし穴その二:Java の Map<String, Integer> の値は Integer の箱なので、fewest も Integer にすると votes.get(a) == fewest は同じ箱かどうかを比べます。
-128 ~ 127 は作り置きの箱を共有するので合っているように見えますが、128 票以上の同点は見逃します。
int に取り出して比べるか equals を使います。
落とし穴その三:過半数の基準は用紙全体ではなくこの回に数えた用紙の数で、ちょうど半分は過半数ではないので 2 * 票 > 数えた数 です。

Pythonmain.py · 25 行
import sys lines = sys.stdin.read().split("\n")c, v = map(int, lines[0].split())remaining = lines[1].split()          # 남은 후보, 처음 적힌 차례대로ballots = [lines[2 + i].split() for i in range(v)] while True:    votes = {name: 0 for name in remaining}   # 0표인 후보도 먼저 넣어 둔다    active = 0                                # 남은 후보를 하나라도 적은 투표지 수    for ballot in ballots:        for name in ballot:            if name in remaining:             # 이 투표지에서 가장 앞에 적힌 남은 후보                votes[name] += 1                active += 1                break    print(" ".join(f"{name}:{votes[name]}" for name in remaining))     leader = max(remaining, key=lambda name: votes[name])    if 2 * votes[leader] > active or len(remaining) == 1:        print(leader)        break    fewest = min(votes.values())    loser = [name for name in remaining if votes[name] == fewest][-1]   # 동점이면 뒤에 적힌 후보    remaining.remove(loser)
JavaScriptmain.js · 29 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const [c, v] = lines[0].trim().split(" ").map(Number);const remaining = lines[1].trim().split(" "); // 남은 후보, 처음 적힌 차례대로const ballots = lines.slice(2, 2 + v).map((line) => line.trim().split(" "));const out = []; while (true) {  const votes = new Map(remaining.map((name) => [name, 0])); // 0표인 후보도 먼저 넣어 둔다  let active = 0; // 남은 후보를 하나라도 적은 투표지 수  for (const ballot of ballots) {    const first = ballot.find((name) => remaining.includes(name)); // 가장 앞에 적힌 남은 후보    if (first !== undefined) {      votes.set(first, votes.get(first) + 1);      active++;    }  }  out.push(remaining.map((name) => `${name}:${votes.get(name)}`).join(" "));   const count = (name) => votes.get(name);  const best = Math.max(...remaining.map(count));  if (2 * best > active || remaining.length === 1) {    out.push(remaining.find((name) => count(name) === best));    break;  }  const fewest = Math.min(...remaining.map(count));  const loser = remaining.filter((name) => count(name) === fewest).pop(); // 동점이면 뒤에 적힌 후보  remaining.splice(remaining.indexOf(loser), 1);}console.log(out.join("\n"));
TypeScriptmain.ts · 30 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const [c, v] = lines[0].trim().split(" ").map(Number);const remaining: string[] = lines[1].trim().split(" "); // 남은 후보, 처음 적힌 차례대로const ballots: string[][] = lines.slice(2, 2 + v).map((line) => line.trim().split(" "));const out: string[] = []; while (true) {  const votes = new Map<string, number>(remaining.map((name): [string, number] => [name, 0])); // 0표인 후보도 먼저 넣어 둔다  let active = 0; // 남은 후보를 하나라도 적은 투표지 수  for (const ballot of ballots) {    const first = ballot.find((name) => remaining.includes(name)); // 가장 앞에 적힌 남은 후보    if (first !== undefined) {      votes.set(first, (votes.get(first) ?? 0) + 1);      active++;    }  }  const count = (name: string): number => votes.get(name) ?? 0;  out.push(remaining.map((name) => `${name}:${count(name)}`).join(" "));   const best = Math.max(...remaining.map(count));  if (2 * best > active || remaining.length === 1) {    out.push(remaining.find((name) => count(name) === best) ?? "");    break;  }  const fewest = Math.min(...remaining.map(count));  const tied = remaining.filter((name) => count(name) === fewest);  remaining.splice(remaining.indexOf(tied[tied.length - 1]), 1); // 동점이면 뒤에 적힌 후보}console.log(out.join("\n"));
JavaMain.java · 47 行
import java.io.*;import java.util.*; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        String[] first = br.readLine().trim().split(" ");        int v = Integer.parseInt(first[1]);        List<String> remaining = new ArrayList<>(Arrays.asList(br.readLine().trim().split(" "))); // 처음 적힌 차례대로        List<String[]> ballots = new ArrayList<>();        for (int i = 0; i < v; i++) ballots.add(br.readLine().trim().split(" "));        StringBuilder out = new StringBuilder();         while (true) {            Map<String, Integer> votes = new HashMap<>();            for (String name : remaining) votes.put(name, 0); // 0표인 후보도 먼저 넣어 둔다            int active = 0; // 남은 후보를 하나라도 적은 투표지 수            for (String[] ballot : ballots) {                for (String name : ballot) {                    if (remaining.contains(name)) { // 가장 앞에 적힌 남은 후보                        votes.merge(name, 1, Integer::sum);                        active++;                        break;                    }                }            }            StringJoiner line = new StringJoiner(" ");            for (String name : remaining) line.add(name + ":" + votes.get(name));            out.append(line).append('\n');             // Integer 상자끼리 ==로 비교하지 않도록 int로 꺼내 둔다            int best = -1, fewest = Integer.MAX_VALUE;            String leader = "", loser = "";            for (String name : remaining) {                int n = votes.get(name);                if (n > best) { best = n; leader = name; }                if (n <= fewest) { fewest = n; loser = name; } // <=: 동점이면 뒤에 적힌 후보            }            if (2 * best > active || remaining.size() == 1) {                out.append(leader).append('\n');                break;            }            remaining.remove(loser);        }        System.out.print(out);    }}
C#Main.cs · 42 行
using System;using System.Collections.Generic;using System.Linq;using System.Text; class Program{    static void Main()    {        int v = int.Parse(Console.ReadLine().Trim().Split(' ')[1]);        var remaining = Console.ReadLine().Trim().Split(' ').ToList(); // 처음 적힌 차례대로        var ballots = new List<string[]>();        for (int i = 0; i < v; i++) ballots.Add(Console.ReadLine().Trim().Split(' '));        var sb = new StringBuilder();         while (true)        {            var votes = remaining.ToDictionary(name => name, name => 0); // 0표인 후보도 먼저 넣어 둔다            int active = 0; // 남은 후보를 하나라도 적은 투표지 수            foreach (string[] ballot in ballots)            {                string first = ballot.FirstOrDefault(name => remaining.Contains(name)); // 가장 앞에 적힌 남은 후보                if (first != null)                {                    votes[first]++;                    active++;                }            }            sb.Append(string.Join(" ", remaining.Select(name => $"{name}:{votes[name]}"))).Append('\n');             int best = remaining.Max(name => votes[name]);            if (2 * best > active || remaining.Count == 1)            {                sb.Append(remaining.First(name => votes[name] == best)).Append('\n');                break;            }            int fewest = remaining.Min(name => votes[name]);            remaining.Remove(remaining.Last(name => votes[name] == fewest)); // 동점이면 뒤에 적힌 후보        }        Console.Write(sb);    }}
C++main.cpp · 52 行
#include <algorithm>#include <iostream>#include <map>#include <sstream>#include <string>#include <vector> int main() {    int c, v;    std::cin >> c >> v;    std::vector<std::string> remaining(c); // 남은 후보, 처음 적힌 차례대로    for (auto& name : remaining) std::cin >> name;    std::string line;    std::getline(std::cin, line); // 후보 줄의 끝    std::vector<std::vector<std::string>> ballots(v);    for (auto& ballot : ballots) {        std::getline(std::cin, line);        std::istringstream in(line);        for (std::string name; in >> name;) ballot.push_back(name);    }    auto is_left = [&](const std::string& name) {        return std::find(remaining.begin(), remaining.end(), name) != remaining.end();    };     while (true) {        std::map<std::string, int> votes;        for (const auto& name : remaining) votes[name] = 0; // 0표인 후보도 먼저 넣어 둔다        int active = 0; // 남은 후보를 하나라도 적은 투표지 수        for (const auto& ballot : ballots) {            auto first = std::find_if(ballot.begin(), ballot.end(), is_left); // 가장 앞에 적힌 남은 후보            if (first != ballot.end()) {                votes[*first]++;                active++;            }        }        for (size_t i = 0; i < remaining.size(); i++)            std::cout << (i ? " " : "") << remaining[i] << ":" << votes[remaining[i]];        std::cout << "\n";         int best = -1, fewest = v + 1;        std::string leader, loser;        for (const auto& name : remaining) {            if (votes[name] > best) { best = votes[name]; leader = name; }            if (votes[name] <= fewest) { fewest = votes[name]; loser = name; } // <=: 동점이면 뒤에 적힌 후보        }        if (2 * best > active || remaining.size() == 1) {            std::cout << leader << "\n";            break;        }        remaining.erase(std::find(remaining.begin(), remaining.end(), loser));    }}
Cmain.c · 63 行
#include <stdio.h>#include <string.h> #define MAXC 20#define MAXV 1000 static char cand[MAXC][11];          /* 후보 이름, 처음 적힌 차례 */static int left[MAXC];               /* 1이면 아직 남은 후보 */static int ballot[MAXV][MAXC];       /* 투표지마다 후보 번호 */static int len[MAXV]; static int cand_id(const char *name, int c) {    for (int i = 0; i < c; i++)        if (strcmp(cand[i], name) == 0) return i;    return -1;} int main(void) {    int c, v;    scanf("%d %d", &c, &v);    for (int i = 0; i < c; i++) {        scanf("%10s", cand[i]);        left[i] = 1;    }    getchar(); /* 후보 줄의 끝 */    char line[256];    for (int b = 0; b < v; b++) {        fgets(line, sizeof line, stdin);        len[b] = 0;        for (char *tok = strtok(line, " \r\n"); tok; tok = strtok(NULL, " \r\n"))            ballot[b][len[b]++] = cand_id(tok, c);    }    int count_left = c;     while (1) {        int votes[MAXC] = {0}; /* 후보 번호마다 표 수 */        int active = 0;        /* 남은 후보를 하나라도 적은 투표지 수 */        for (int b = 0; b < v; b++) {            for (int k = 0; k < len[b]; k++) {                if (left[ballot[b][k]]) { /* 가장 앞에 적힌 남은 후보 */                    votes[ballot[b][k]]++;                    active++;                    break;                }            }        }        int best = -1, fewest = v + 1, leader = -1, loser = -1, printed = 0;        for (int i = 0; i < c; i++) {            if (!left[i]) continue;            printf("%s%s:%d", printed++ ? " " : "", cand[i], votes[i]);            if (votes[i] > best) { best = votes[i]; leader = i; }            if (votes[i] <= fewest) { fewest = votes[i]; loser = i; } /* <=: 동점이면 뒤에 적힌 후보 */        }        putchar('\n');        if (2 * best > active || count_left == 1) {            printf("%s\n", cand[leader]);            break;        }        left[loser] = 0;        count_left--;    }    return 0;}
Craykonmain.cray · 62 行
first = コンソール.行読み().空白除去().分ける();v = first[1].数に();remaining = コンソール.行読み().空白除去().分ける(); // 남은 후보, 처음 적힌 차례대로ballots = [];繰り返す (i = 0; i < v; i++){    ballots.追加(コンソール.行読み().空白除去().分ける());} 끝남 = 偽;間 (!끝남){    votes = 辞書();    それぞれ (name の中 remaining)    {        votes[name] = 0; // 0표인 후보도 먼저 넣어 둔다    }    active = 0; // 남은 후보를 하나라도 적은 투표지 수    それぞれ (ballot の中 ballots)    {        それぞれ (name の中 ballot)        {            もし (remaining.含む(name))            {                votes[name] = votes[name] + 1;                active++;                抜ける;            }        }    }     parts = [];    best = -1;    fewest = v + 1;    leader = "";    loser = "";    それぞれ (name の中 remaining)    {        parts.追加(name + ":" + votes[name]);        もし (votes[name] > best)        {            best = votes[name];            leader = name;        }        もし (votes[name] <= fewest)        {            fewest = votes[name];            loser = name;        }    }    コンソール.書く(parts.つなぐ(" "));     もし (2 * best > active || remaining.個数 == 1)    {        コンソール.書く(leader);        끝남 = 真;    }    でなければ    {        remaining.取り除く(loser);    }}