{ }codelper
广度优先搜索队列P0188

邻居多的一方占领的颜料晕染

在 R 行 C 列的纸上滴下几滴颜料。
'.' 是空格,'#' 是颜料渗不进去的格子,英文大写字母是该字母颜色的颜料将要滴落的格子。
给出每个字母的颜料滴落的时刻(分钟)。
颜料滴落之前,那个格子也算空格。
两个格子上下左右相接时互为邻居。
从第 0 分钟开始,每分钟按顺序做下面两步。
第 1 步(晕开):凡是在上一分钟结束时至少有一个带字母的邻居的空格,都只根据上一分钟结束时的状态,同时发生变化。
该空格变成其邻居中出现次数最多的字母;如果出现次数最多的字母有两个或以上,就变成 '*'。
'*' 格和 '#' 格不算字母,'*' 格之后不再变化。
第 2 步(滴落):这一分钟滴落的颜料,只有当自己的格子仍是空格时,才把那个格子变成自己的字母。
如果自己的格子已经变成别的字母或 '*',这滴颜料就当作没有滴过。
第 0 分钟没有上一分钟,所以只滴落。
一旦变成字母的格子不会再变化。
当没有格子会再变化、也没有要滴落的颜料时,输出纸的样子。
滴落的颜料和晕开的格子输出其字母,平局的格子输出 '*',始终是空格的输出 '.','#' 原样输出。

输入
第一行给出 R 和 C,接下来 R 行是由 '.'、'#'、英文大写字母组成的长度为 C 的字符串,下一行给出颜料滴数 K,接下来 K 行每行是字母 L 和滴落时刻 t,用空格分开。
1 ≤ R, C ≤ 50,0 ≤ K ≤ 26,0 ≤ t ≤ 10,000。
地图上的大写字母互不相同,K 行中的字母与地图上的大写字母完全一致(顺序任意)。
输出
R 行,每行是长度为 C 的字符串(最终纸的样子)
样例 1 · stdin
3 5
A...B
.....
..#..
2
A 0
B 0
stdout
AA*BB
AA*BB
AA#BB
样例 2 · stdin
4 4
.A#.
....
B...
...C
3
A 3
B 0
C 2
stdout
BB#B
BBBB
BBB*
BBBC
main.py
输入(stdin)

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

$ 就绪

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

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

从多处同时扩散的问题,用一层一层的广度优先搜索(BFS)。
队列里只放上一分钟新涂上的格子(一层),把它们相邻的空格收集为这一分钟的候选,对每个候选数邻居的字母,全部决定之后再同时涂上。
队列里只需要上一分钟涂上的格子,是因为更早涂上的格子旁边的空格,早在下一分钟就已经变了。
最常见的陷阱是像普通 BFS 那样,一从队列取出就立刻涂邻居——这样先取出的一方直接占领格子,根本不和同一分钟到达的其他颜色较量,平局('*')和多数决都消失了。
其他陷阱:平局时给字母表靠前的字母、把 '*' 放进队列让 '*' 扩散、先滴落再晕开(那样恰好在颜料晕到那一分钟滴落的颜料不会被盖住而存活下来)。
没有东西在扩散时,不必一分钟一分钟地数,直接跳到下一滴颜料的时刻即可(老老实实数完 10,000 分钟也不慢)。
为了同一分钟不把一个格子两次放进候选,把"本分钟已放入"的标记记成分钟编号,这样每分钟都不用清除。
表的处理:Python 用 list(row) 把一行变成字符列表,用 dict 计数。
Java 用 char[][] 和 int[26] 计数表,开始时刻用 Comparator.comparingInt(Start::t) 排序。
C# 用 char[][]、new int[r, c](矩形数组)、具名元组列表和 OrderBy。
C++ 用 std::vector<std::string> 和 std::array<int, 26>,用 std::max_element、std::count 找出最多的字母和平局。
C 用固定大小的数组和 qsort(比较函数返回负数、0 或正数)。
JavaScript、TypeScript 用 Map 计数,再用 Math.max(...votes.values())。
Craykon 把地图存成单字符字符串的二维列表,用 字典() 计数,遍历 votes.键列表 找出最大的次数以及它出现了几次。
开始时刻排序时像 starts.排序(时刻顺) 这样传入比较函数,一行用 row.连接("") 连起来输出。

Pythonmain.py · 56 行
import sys data = sys.stdin.read().split("\n")r, c = map(int, data[0].split())grid = [list(data[1 + y].strip()) for y in range(r)]k = int(data[1 + r])starts = []  # (시작 분, 글자, y, x)where = {grid[y][x]: (y, x) for y in range(r) for x in range(c) if grid[y][x].isupper()}for line in data[2 + r:2 + r + k]:    letter, t = line.split()    starts.append((int(t), letter, *where[letter]))    grid[where[letter][0]][where[letter][1]] = "."  # 시작하기 전에는 빈칸이다starts.sort() STEPS = ((-1, 0), (1, 0), (0, -1), (0, 1))frontier = []  # 직전 분에 새로 글자를 얻은 칸들(너비 우선 탐색의 한 겹)nxt = 0        # 아직 시작하지 않은 첫 출발점minute = 0while frontier or nxt < len(starts):    if not frontier:        minute = max(minute, starts[nxt][0])  # 퍼질 것이 없으면 다음 출발 시각으로 건너뛴다    # 1) 퍼지기: 직전 분 상태로 모든 빈칸을 한꺼번에 정한다    candidates = []    seen = set()    for y, x in frontier:        for dy, dx in STEPS:            ny, nx = y + dy, x + dx            if 0 <= ny < r and 0 <= nx < c and grid[ny][nx] == "." and (ny, nx) not in seen:                seen.add((ny, nx))                candidates.append((ny, nx))    decided = []    for y, x in candidates:        votes = {}        for dy, dx in STEPS:            ny, nx = y + dy, x + dx            if 0 <= ny < r and 0 <= nx < c and grid[ny][nx].isupper():                votes[grid[ny][nx]] = votes.get(grid[ny][nx], 0) + 1        best = max(votes.values())        winners = [ch for ch, v in votes.items() if v == best]        decided.append((y, x, winners[0] if len(winners) == 1 else "*"))    new_frontier = []    for y, x, ch in decided:        grid[y][x] = ch        if ch != "*":            new_frontier.append((y, x))  # '*'는 퍼지지 않는다    # 2) 시작: 이 분에 시작하는 출발점은 칸이 아직 빈칸일 때만 시작한다    while nxt < len(starts) and starts[nxt][0] == minute:        _, letter, y, x = starts[nxt]        if grid[y][x] == ".":            grid[y][x] = letter            new_frontier.append((y, x))        nxt += 1    frontier = new_frontier    minute += 1 print("\n".join("".join(row) for row in grid))
JavaScriptmain.js · 67 行
const data = require("fs").readFileSync(0, "utf8").split("\n");const [r, c] = data[0].trim().split(/\s+/).map(Number);const grid = [];const where = new Map(); // 글자 → 칸 번호(y * c + x)for (let y = 0; y < r; y++) {  const row = data[1 + y].trim().split("");  row.forEach((ch, x) => {    if (ch >= "A" && ch <= "Z") {      where.set(ch, y * c + x);      row[x] = "."; // 시작하기 전에는 빈칸이다    }  });  grid.push(row);}const k = Number(data[1 + r]);const starts = []; // { t, letter, cell }for (let i = 0; i < k; i++) {  const [letter, t] = data[2 + r + i].trim().split(/\s+/);  starts.push({ t: Number(t), letter, cell: where.get(letter) });}starts.sort((a, b) => a.t - b.t); const isLetter = (ch) => ch >= "A" && ch <= "Z";const STEPS = [[-1, 0], [1, 0], [0, -1], [0, 1]];const mark = new Array(r * c).fill(-1); // 이번 분에 이미 후보로 넣은 칸let frontier = []; // 직전 분에 새로 글자를 얻은 칸들(너비 우선 탐색의 한 겹)let next = 0;let minute = 0;while (frontier.length > 0 || next < starts.length) {  if (frontier.length === 0) minute = Math.max(minute, starts[next].t); // 퍼질 것이 없으면 건너뛴다  // 1) 퍼지기: 직전 분 상태로 모든 빈칸을 한꺼번에 정한다  const decided = [];  for (const cell of frontier) {    const y = Math.floor(cell / c), x = cell % c;    for (const [dy, dx] of STEPS) {      const ny = y + dy, nx = x + dx;      if (ny < 0 || ny >= r || nx < 0 || nx >= c || grid[ny][nx] !== "." || mark[ny * c + nx] === minute) continue;      mark[ny * c + nx] = minute;      const votes = new Map();      for (const [ey, ex] of STEPS) {        const vy = ny + ey, vx = nx + ex;        if (vy >= 0 && vy < r && vx >= 0 && vx < c && isLetter(grid[vy][vx]))          votes.set(grid[vy][vx], (votes.get(grid[vy][vx]) ?? 0) + 1);      }      const best = Math.max(...votes.values());      const winners = [...votes].filter(([, v]) => v === best);      decided.push([ny, nx, winners.length === 1 ? winners[0][0] : "*"]);    }  }  const newFrontier = [];  for (const [y, x, ch] of decided) {    grid[y][x] = ch;    if (ch !== "*") newFrontier.push(y * c + x); // '*'는 퍼지지 않는다  }  // 2) 시작: 이 분에 시작하는 출발점은 칸이 아직 빈칸일 때만 시작한다  while (next < starts.length && starts[next].t === minute) {    const { letter, cell } = starts[next++];    const y = Math.floor(cell / c), x = cell % c;    if (grid[y][x] === ".") {      grid[y][x] = letter;      newFrontier.push(cell);    }  }  frontier = newFrontier;  minute++;}console.log(grid.map((row) => row.join("")).join("\n"));
TypeScriptmain.ts · 74 行
import { readFileSync } from "node:fs"; const data: string[] = readFileSync(0, "utf8").split("\n");const [r, c] = data[0].trim().split(/\s+/).map(Number);const grid: string[][] = [];const where = new Map<string, number>(); // 글자 → 칸 번호(y * c + x)for (let y = 0; y < r; y++) {  const row = data[1 + y].trim().split("");  row.forEach((ch, x) => {    if (ch >= "A" && ch <= "Z") {      where.set(ch, y * c + x);      row[x] = "."; // 시작하기 전에는 빈칸이다    }  });  grid.push(row);}const k = Number(data[1 + r]);interface Start {  t: number;  letter: string;  cell: number;}const starts: Start[] = [];for (let i = 0; i < k; i++) {  const [letter, t] = data[2 + r + i].trim().split(/\s+/);  starts.push({ t: Number(t), letter, cell: where.get(letter)! });}starts.sort((a, b) => a.t - b.t); const isLetter = (ch: string): boolean => ch >= "A" && ch <= "Z";const STEPS: [number, number][] = [[-1, 0], [1, 0], [0, -1], [0, 1]];const mark: number[] = new Array<number>(r * c).fill(-1); // 이번 분에 이미 후보로 넣은 칸let frontier: number[] = []; // 직전 분에 새로 글자를 얻은 칸들(너비 우선 탐색의 한 겹)let next = 0;let minute = 0;while (frontier.length > 0 || next < starts.length) {  if (frontier.length === 0) minute = Math.max(minute, starts[next].t); // 퍼질 것이 없으면 건너뛴다  // 1) 퍼지기: 직전 분 상태로 모든 빈칸을 한꺼번에 정한다  const decided: [number, number, string][] = [];  for (const cell of frontier) {    const y = Math.floor(cell / c), x = cell % c;    for (const [dy, dx] of STEPS) {      const ny = y + dy, nx = x + dx;      if (ny < 0 || ny >= r || nx < 0 || nx >= c || grid[ny][nx] !== "." || mark[ny * c + nx] === minute) continue;      mark[ny * c + nx] = minute;      const votes = new Map<string, number>();      for (const [ey, ex] of STEPS) {        const vy = ny + ey, vx = nx + ex;        if (vy >= 0 && vy < r && vx >= 0 && vx < c && isLetter(grid[vy][vx]))          votes.set(grid[vy][vx], (votes.get(grid[vy][vx]) ?? 0) + 1);      }      const best = Math.max(...votes.values());      const winners = [...votes].filter(([, v]) => v === best);      decided.push([ny, nx, winners.length === 1 ? winners[0][0] : "*"]);    }  }  const newFrontier: number[] = [];  for (const [y, x, ch] of decided) {    grid[y][x] = ch;    if (ch !== "*") newFrontier.push(y * c + x); // '*'는 퍼지지 않는다  }  // 2) 시작: 이 분에 시작하는 출발점은 칸이 아직 빈칸일 때만 시작한다  while (next < starts.length && starts[next].t === minute) {    const { letter, cell } = starts[next++];    const y = Math.floor(cell / c), x = cell % c;    if (grid[y][x] === ".") {      grid[y][x] = letter;      newFrontier.push(cell);    }  }  frontier = newFrontier;  minute++;}console.log(grid.map((row) => row.join("")).join("\n"));
JavaMain.java · 84 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.Arrays;import java.util.Comparator;import java.util.List; public class Main {    record Start(int t, char letter, int y, int x) {}     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        String[] first = in.readLine().trim().split("\\s+");        int r = Integer.parseInt(first[0]), c = Integer.parseInt(first[1]);        char[][] grid = new char[r][];        int[][] where = new int[26][];        for (int y = 0; y < r; y++) {            grid[y] = in.readLine().trim().toCharArray();            for (int x = 0; x < c; x++)                if (Character.isUpperCase(grid[y][x])) {                    where[grid[y][x] - 'A'] = new int[] {y, x};                    grid[y][x] = '.'; // 시작하기 전에는 빈칸이다                }        }        int k = Integer.parseInt(in.readLine().trim());        List<Start> starts = new ArrayList<>();        for (int i = 0; i < k; i++) {            String[] p = in.readLine().trim().split("\\s+");            char letter = p[0].charAt(0);            int[] pos = where[letter - 'A'];            starts.add(new Start(Integer.parseInt(p[1]), letter, pos[0], pos[1]));        }        starts.sort(Comparator.comparingInt(Start::t));         int[] dy = {-1, 1, 0, 0}, dx = {0, 0, -1, 1};        int[][] mark = new int[r][c]; // 이번 분에 이미 후보로 넣은 칸        for (int[] row : mark) Arrays.fill(row, -1);        List<int[]> frontier = new ArrayList<>(); // 직전 분에 새로 글자를 얻은 칸들(너비 우선 탐색의 한 겹)        int next = 0, minute = 0;        while (!frontier.isEmpty() || next < k) {            if (frontier.isEmpty()) minute = Math.max(minute, starts.get(next).t()); // 퍼질 것이 없으면 건너뛴다            // 1) 퍼지기: 직전 분 상태로 모든 빈칸을 한꺼번에 정한다            List<int[]> decided = new ArrayList<>(); // {y, x, 글자}            for (int[] cell : frontier)                for (int d = 0; d < 4; d++) {                    int ny = cell[0] + dy[d], nx = cell[1] + dx[d];                    if (ny < 0 || ny >= r || nx < 0 || nx >= c || grid[ny][nx] != '.' || mark[ny][nx] == minute) continue;                    mark[ny][nx] = minute;                    int[] votes = new int[26];                    for (int e = 0; e < 4; e++) {                        int vy = ny + dy[e], vx = nx + dx[e];                        if (vy >= 0 && vy < r && vx >= 0 && vx < c && Character.isUpperCase(grid[vy][vx]))                            votes[grid[vy][vx] - 'A']++;                    }                    int best = 0, winners = 0;                    char win = '*';                    for (int l = 0; l < 26; l++) {                        if (votes[l] > best) { best = votes[l]; winners = 1; win = (char) ('A' + l); }                        else if (votes[l] == best && best > 0) winners++;                    }                    decided.add(new int[] {ny, nx, winners == 1 ? win : '*'});                }            List<int[]> fresh = new ArrayList<>();            for (int[] dcd : decided) {                grid[dcd[0]][dcd[1]] = (char) dcd[2];                if (dcd[2] != '*') fresh.add(new int[] {dcd[0], dcd[1]}); // '*'는 퍼지지 않는다            }            // 2) 시작: 이 분에 시작하는 출발점은 칸이 아직 빈칸일 때만 시작한다            for (; next < k && starts.get(next).t() == minute; next++) {                Start s = starts.get(next);                if (grid[s.y()][s.x()] == '.') {                    grid[s.y()][s.x()] = s.letter();                    fresh.add(new int[] {s.y(), s.x()});                }            }            frontier = fresh;            minute++;        }        StringBuilder out = new StringBuilder();        for (char[] row : grid) out.append(row).append('\n');        System.out.print(out);    }}
C#Main.cs · 86 行
using System;using System.Collections.Generic;using System.Linq;using System.Text; class Program{    static void Main()    {        int[] first = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();        int r = first[0], c = first[1];        var grid = new char[r][];        var where = new Dictionary<char, (int y, int x)>();        for (int y = 0; y < r; y++)        {            grid[y] = Console.ReadLine().Trim().ToCharArray();            for (int x = 0; x < c; x++)                if (char.IsUpper(grid[y][x]))                {                    where[grid[y][x]] = (y, x);                    grid[y][x] = '.'; // 시작하기 전에는 빈칸이다                }        }        int k = int.Parse(Console.ReadLine().Trim());        var starts = new List<(int t, char letter, int y, int x)>();        for (int i = 0; i < k; i++)        {            string[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);            char letter = p[0][0];            var (sy, sx) = where[letter];            starts.Add((int.Parse(p[1]), letter, sy, sx));        }        starts = starts.OrderBy(s => s.t).ToList();         int[] dy = { -1, 1, 0, 0 }, dx = { 0, 0, -1, 1 };        bool Inside(int y, int x) => y >= 0 && y < r && x >= 0 && x < c;        var mark = new int[r, c]; // 이번 분에 이미 후보로 넣은 칸        for (int y = 0; y < r; y++)            for (int x = 0; x < c; x++) mark[y, x] = -1;        var frontier = new List<(int y, int x)>(); // 직전 분에 새로 글자를 얻은 칸들(너비 우선 탐색의 한 겹)        int next = 0, minute = 0;        while (frontier.Count > 0 || next < k)        {            if (frontier.Count == 0) minute = Math.Max(minute, starts[next].t); // 퍼질 것이 없으면 건너뛴다            // 1) 퍼지기: 직전 분 상태로 모든 빈칸을 한꺼번에 정한다            var decided = new List<(int y, int x, char ch)>();            foreach (var (y, x) in frontier)                for (int d = 0; d < 4; d++)                {                    int ny = y + dy[d], nx = x + dx[d];                    if (!Inside(ny, nx) || grid[ny][nx] != '.' || mark[ny, nx] == minute) continue;                    mark[ny, nx] = minute;                    var votes = new int[26];                    for (int e = 0; e < 4; e++)                    {                        int vy = ny + dy[e], vx = nx + dx[e];                        if (Inside(vy, vx) && char.IsUpper(grid[vy][vx])) votes[grid[vy][vx] - 'A']++;                    }                    int best = votes.Max();                    bool single = votes.Count(v => v == best) == 1;                    decided.Add((ny, nx, single ? (char)('A' + Array.IndexOf(votes, best)) : '*'));                }            var fresh = new List<(int y, int x)>();            foreach (var (y, x, ch) in decided)            {                grid[y][x] = ch;                if (ch != '*') fresh.Add((y, x)); // '*'는 퍼지지 않는다            }            // 2) 시작: 이 분에 시작하는 출발점은 칸이 아직 빈칸일 때만 시작한다            for (; next < k && starts[next].t == minute; next++)            {                var s = starts[next];                if (grid[s.y][s.x] == '.')                {                    grid[s.y][s.x] = s.letter;                    fresh.Add((s.y, s.x));                }            }            frontier = fresh;            minute++;        }        var out_ = new StringBuilder();        foreach (var row in grid) out_.Append(row).Append('\n');        Console.Write(out_);    }}
C++main.cpp · 78 行
#include <algorithm>#include <cctype>#include <array>#include <iostream>#include <string>#include <tuple>#include <vector> struct Start {    int t;    char letter;    int y, x;}; int main() {    int r, c, k;    std::cin >> r >> c;    std::vector<std::string> grid(r);    std::array<std::pair<int, int>, 26> where{};    for (int y = 0; y < r; y++) {        std::cin >> grid[y];        for (int x = 0; x < c; x++)            if (std::isupper(static_cast<unsigned char>(grid[y][x]))) {                where[grid[y][x] - 'A'] = {y, x};                grid[y][x] = '.';  // 시작하기 전에는 빈칸이다            }    }    std::cin >> k;    std::vector<Start> starts(k);    for (auto& s : starts) {        std::cin >> s.letter >> s.t;        std::tie(s.y, s.x) = where[s.letter - 'A'];    }    std::sort(starts.begin(), starts.end(), [](const Start& a, const Start& b) { return a.t < b.t; });     const int DY[4] = {-1, 1, 0, 0}, DX[4] = {0, 0, -1, 1};    auto inside = [&](int y, int x) { return y >= 0 && y < r && x >= 0 && x < c; };    std::vector<std::vector<int>> mark(r, std::vector<int>(c, -1));  // 이번 분에 이미 후보로 넣은 칸    std::vector<std::pair<int, int>> frontier;  // 직전 분에 새로 글자를 얻은 칸들(너비 우선 탐색의 한 겹)    std::size_t next = 0;    int minute = 0;    while (!frontier.empty() || next < starts.size()) {        if (frontier.empty()) minute = std::max(minute, starts[next].t);  // 퍼질 것이 없으면 건너뛴다        // 1) 퍼지기: 직전 분 상태로 모든 빈칸을 한꺼번에 정한다        std::vector<std::tuple<int, int, char>> decided;        for (auto [y, x] : frontier)            for (int d = 0; d < 4; d++) {                int ny = y + DY[d], nx = x + DX[d];                if (!inside(ny, nx) || grid[ny][nx] != '.' || mark[ny][nx] == minute) continue;                mark[ny][nx] = minute;                std::array<int, 26> votes{};                for (int e = 0; e < 4; e++) {                    int vy = ny + DY[e], vx = nx + DX[e];                    if (inside(vy, vx) && std::isupper(static_cast<unsigned char>(grid[vy][vx]))) votes[grid[vy][vx] - 'A']++;                }                int best = *std::max_element(votes.begin(), votes.end());                int winners = static_cast<int>(std::count(votes.begin(), votes.end(), best));                char win = static_cast<char>('A' + (std::max_element(votes.begin(), votes.end()) - votes.begin()));                decided.emplace_back(ny, nx, winners == 1 ? win : '*');            }        std::vector<std::pair<int, int>> fresh;        for (auto [y, x, ch] : decided) {            grid[y][x] = ch;            if (ch != '*') fresh.emplace_back(y, x);  // '*'는 퍼지지 않는다        }        // 2) 시작: 이 분에 시작하는 출발점은 칸이 아직 빈칸일 때만 시작한다        for (; next < starts.size() && starts[next].t == minute; next++) {            const Start& s = starts[next];            if (grid[s.y][s.x] == '.') {                grid[s.y][s.x] = s.letter;                fresh.emplace_back(s.y, s.x);            }        }        frontier = std::move(fresh);        minute++;    }    for (const auto& row : grid) std::cout << row << "\n";}
Cmain.c · 97 行
#include <stdio.h>#include <stdlib.h> #define MAXN 50 static char grid[MAXN][MAXN + 2];static int mark[MAXN * MAXN];       /* 이번 분에 이미 후보로 넣은 칸 */static int frontier[MAXN * MAXN], nf; /* 직전 분에 새로 글자를 얻은 칸들(너비 우선 탐색의 한 겹) */static int fresh[MAXN * MAXN], nfresh;static int decY[MAXN * MAXN], decX[MAXN * MAXN];static char decCh[MAXN * MAXN]; typedef struct {    int t;    char letter;    int cell;} Start; static int byTime(const void *a, const void *b) {    return ((const Start *)a)->t - ((const Start *)b)->t;} int main(void) {    int r, c, k;    int where[26];    Start starts[26];    scanf("%d %d", &r, &c);    for (int y = 0; y < r; y++) {        scanf("%51s", grid[y]);        for (int x = 0; x < c; x++)            if (grid[y][x] >= 'A' && grid[y][x] <= 'Z') {                where[grid[y][x] - 'A'] = y * c + x;                grid[y][x] = '.'; /* 시작하기 전에는 빈칸이다 */            }    }    scanf("%d", &k);    for (int i = 0; i < k; i++) {        char letter[4];        scanf("%3s %d", letter, &starts[i].t);        starts[i].letter = letter[0];        starts[i].cell = where[letter[0] - 'A'];    }    qsort(starts, k, sizeof starts[0], byTime);    for (int i = 0; i < r * c; i++) mark[i] = -1;     const int DY[4] = {-1, 1, 0, 0}, DX[4] = {0, 0, -1, 1};    int next = 0, minute = 0;    nf = 0;    while (nf > 0 || next < k) {        if (nf == 0 && starts[next].t > minute) minute = starts[next].t; /* 퍼질 것이 없으면 건너뛴다 */        /* 1) 퍼지기: 직전 분 상태로 모든 빈칸을 한꺼번에 정한다 */        int nd = 0;        for (int i = 0; i < nf; i++) {            int y = frontier[i] / c, x = frontier[i] % c;            for (int d = 0; d < 4; d++) {                int ny = y + DY[d], nx = x + DX[d];                if (ny < 0 || ny >= r || nx < 0 || nx >= c || grid[ny][nx] != '.' || mark[ny * c + nx] == minute) continue;                mark[ny * c + nx] = minute;                int votes[26] = {0};                for (int e = 0; e < 4; e++) {                    int vy = ny + DY[e], vx = nx + DX[e];                    if (vy >= 0 && vy < r && vx >= 0 && vx < c && grid[vy][vx] >= 'A' && grid[vy][vx] <= 'Z')                        votes[grid[vy][vx] - 'A']++;                }                int best = 0, winners = 0;                char win = '*';                for (int l = 0; l < 26; l++) {                    if (votes[l] > best) { best = votes[l]; winners = 1; win = (char)('A' + l); }                    else if (votes[l] == best && best > 0) winners++;                }                decY[nd] = ny;                decX[nd] = nx;                decCh[nd] = winners == 1 ? win : '*';                nd++;            }        }        nfresh = 0;        for (int i = 0; i < nd; i++) {            grid[decY[i]][decX[i]] = decCh[i];            if (decCh[i] != '*') fresh[nfresh++] = decY[i] * c + decX[i]; /* '*'는 퍼지지 않는다 */        }        /* 2) 시작: 이 분에 시작하는 출발점은 칸이 아직 빈칸일 때만 시작한다 */        while (next < k && starts[next].t == minute) {            int y = starts[next].cell / c, x = starts[next].cell % c;            if (grid[y][x] == '.') {                grid[y][x] = starts[next].letter;                fresh[nfresh++] = starts[next].cell;            }            next++;        }        for (int i = 0; i < nfresh; i++) frontier[i] = fresh[i];        nf = nfresh;        minute++;    }    for (int y = 0; y < r; y++) puts(grid[y]);    return 0;}
Craykonmain.cray · 145 行
첫줄 = 控制台.读数字();r = 첫줄[0];c = 첫줄[1]; 글자인가(ch){    返回 ch >= "A" && ch <= "Z";} // 칸은 수 하나(y * c + x)로 다룬다. 지도는 한 글자짜리 문자열의 2차원 목록grid = [];where = 字典(); // 글자 → 칸 번호循环 (y = 0; y < r; y++){    줄 = 控制台.读行().去空白();    row = [];    循环 (x = 0; x < c; x++)    {        ch = 줄[x];        如果 (글자인가(ch))        {            where[ch] = y * c + x;            ch = "."; // 시작하기 전에는 빈칸이다        }        row.添加(ch);    }    grid.添加(row);}k = 控制台.读行().去空白().转数字();starts = []; // [시작 분, 글자, 칸 번호]循环 (i = 0; i < k; i++){    p = 控制台.读行().去空白().分割(" ");    starts.添加([p[1].转数字(), p[0], where[p[0]]]);} 시각순(앞, 뒤){    返回 앞[0] - 뒤[0];} starts.排序(시각순); DY = [-1, 1, 0, 0];DX = [0, 0, -1, 1]; 안인가(y, x){    返回 y >= 0 && y < r && x >= 0 && x < c;} mark = 新列表(r * c, -1); // 이번 분에 이미 후보로 넣은 칸frontier = []; // 직전 분에 새로 글자를 얻은 칸들(너비 우선 탐색의 한 겹)next = 0;minute = 0;当 (frontier.数量 > 0 || next < k){    如果 (frontier.数量 == 0)    {        minute = 数学.较大值(minute, starts[next][0]); // 퍼질 것이 없으면 건너뛴다    }    // 1) 퍼지기: 직전 분 상태로 모든 빈칸을 한꺼번에 정한다    decided = []; // [칸 번호, 글자]    遍历 (cell 在 frontier)    {        y = 数学.商(cell, c);        x = cell % c;        循环 (d = 0; d < 4; d++)        {            ny = y + DY[d];            nx = x + DX[d];            如果 (안인가(ny, nx) && grid[ny][nx] == "." && mark[ny * c + nx] != minute)            {                mark[ny * c + nx] = minute;                votes = 字典();                循环 (e = 0; e < 4; e++)                {                    vy = ny + DY[e];                    vx = nx + DX[e];                    如果 (안인가(vy, vx) && 글자인가(grid[vy][vx]))                    {                        ch = grid[vy][vx];                        如果 (votes.包含(ch))                        {                            votes[ch] += 1;                        }                        否则                        {                            votes[ch] = 1;                        }                    }                }                best = 0;                winners = 0;                win = "*";                遍历 (ch 在 votes.键列表)                {                    如果 (votes[ch] > best)                    {                        best = votes[ch];                        winners = 1;                        win = ch;                    }                    否则 如果 (votes[ch] == best)                    {                        winners += 1;                    }                }                如果 (winners > 1)                {                    win = "*";                }                decided.添加([ny * c + nx, win]);            }        }    }    fresh = [];    遍历 (dcd 在 decided)    {        grid[数学.商(dcd[0], c)][dcd[0] % c] = dcd[1];        如果 (dcd[1] != "*")        {            fresh.添加(dcd[0]); // '*'는 퍼지지 않는다        }    }    // 2) 시작: 이 분에 시작하는 출발점은 칸이 아직 빈칸일 때만 시작한다    当 (next < k && starts[next][0] == minute)    {        cell = starts[next][2];        y = 数学.商(cell, c);        x = cell % c;        如果 (grid[y][x] == ".")        {            grid[y][x] = starts[next][1];            fresh.添加(cell);        }        next += 1;    }    frontier = fresh;    minute += 1;}遍历 (row 在 grid){    控制台.输出(row.连接(""));}