縦 h マス、横 w マスの盤に駒が置かれている。 マスは .(空き)か、駒一つを表す文字 R、B、Q、N、K のどれかである。 すべての駒は互いに敵で、どの駒もほかの駒を攻撃できる。 R は自分と同じ横の列か同じ縦の列にある駒を攻撃する。 B は自分と同じ斜めの線上にある駒、つまり行番号の差と列番号の差の大きさが等しい駒を攻撃する。 Q は R が攻撃する駒と B が攻撃する駒をすべて攻撃する。 ただし R、B、Q は、自分と相手の駒の間のマス(二つの駒があるマスを除く、二つを結ぶまっすぐな列または斜めの線上のマス)にほかの駒が一つでもあると、その相手の駒を攻撃できない。 つまり、ほかの駒を飛び越えて攻撃することはできない。 N は横に 1 マス・縦に 2 マス、または横に 2 マス・縦に 1 マス離れた駒を攻撃し、間にある駒には邪魔されない。 K は縦、横、斜めにすぐ隣り合う八つのマスの駒を攻撃する。 盤が t 個与えられるとき、盤ごとに、ほかのどの駒からも攻撃されない駒の個数を求めよ。 一行目に t が与えられる。 盤ごとに、最初の行に h と w が空白一つで区切って与えられ、続く h 行に長さ w の文字列が与えられる。 盤ごとに答えを一行に一つずつ出力する。 駒のない盤の答えは 0 である。
入力
1 ≤ t ≤ 30。 1 ≤ h, w ≤ 12。 一つの盤の駒は 0 個以上 40 個以下。 盤の文字列には .、R、B、Q、N、K だけがあり、空白はない
import sysdef straight(a, b): """같은 가로줄이나 세로줄에 있는가""" return a[0] == b[0] or a[1] == b[1]def diagonal(a, b): """같은 대각선에 있는가""" return abs(a[0] - b[0]) == abs(a[1] - b[1])def sign(x): return (x > 0) - (x < 0)def clear(board, a, b): """a와 b 사이(양 끝은 빼고)에 말이 하나도 없는가""" dr, dc = sign(b[0] - a[0]), sign(b[1] - a[1]) r, c = a[0] + dr, a[1] + dc while (r, c) != b: if board[r][c] != ".": return False r, c = r + dr, c + dc return Truedef attacks(board, kind, a, b): """a 칸에 있는 kind 말이 b 칸을 공격하는가""" dr, dc = abs(a[0] - b[0]), abs(a[1] - b[1]) if kind == "N": return (dr == 1 and dc == 2) or (dr == 2 and dc == 1) if kind == "K": return max(dr, dc) == 1 if kind == "R": return straight(a, b) and clear(board, a, b) if kind == "B": return diagonal(a, b) and clear(board, a, b) return (straight(a, b) or diagonal(a, b)) and clear(board, a, b)def is_safe(board, pieces, target): """다른 어떤 말도 target을 공격하지 않는가""" return not any(attacks(board, board[p[0]][p[1]], p, target) for p in pieces if p != target)lines = sys.stdin.read().split("\n")t = int(lines[0])at = 1out = []for _ in range(t): h, w = map(int, lines[at].split()) board = [lines[at + 1 + r].strip() for r in range(h)] at += 1 + h pieces = [(r, c) for r in range(h) for c in range(w) if board[r][c] != "."] out.append(sum(1 for p in pieces if is_safe(board, pieces, p)))print("\n".join(map(str, out)))
JavaScriptmain.js · 52 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");// 같은 가로줄이나 세로줄에 있는가const straight = (a, b) => a.r === b.r || a.c === b.c;// 같은 대각선에 있는가const diagonal = (a, b) => Math.abs(a.r - b.r) === Math.abs(a.c - b.c);// a와 b 사이(양 끝은 빼고)에 말이 하나도 없는가function clear(board, a, b) { const dr = Math.sign(b.r - a.r), dc = Math.sign(b.c - a.c); let r = a.r + dr, c = a.c + dc; while (r !== b.r || c !== b.c) { if (board[r][c] !== ".") return false; r += dr; c += dc; } return true;}// a 칸에 있는 말이 b 칸을 공격하는가function attacks(board, a, b) { const dr = Math.abs(a.r - b.r), dc = Math.abs(a.c - b.c); switch (board[a.r][a.c]) { case "N": return (dr === 1 && dc === 2) || (dr === 2 && dc === 1); case "K": return Math.max(dr, dc) === 1; case "R": return straight(a, b) && clear(board, a, b); case "B": return diagonal(a, b) && clear(board, a, b); default: return (straight(a, b) || diagonal(a, b)) && clear(board, a, b); }}// 다른 어떤 말도 target을 공격하지 않는가const isSafe = (board, pieces, target) => !pieces.some((p) => p !== target && attacks(board, p, target));const t = Number(lines[0]);let at = 1;const out = [];for (let k = 0; k < t; k++) { const [h, w] = lines[at].trim().split(" ").map(Number); const board = lines.slice(at + 1, at + 1 + h).map((s) => s.trim()); at += 1 + h; const pieces = []; for (let r = 0; r < h; r++) { for (let c = 0; c < w; c++) { if (board[r][c] !== ".") pieces.push({ r, c }); } } out.push(pieces.filter((p) => isSafe(board, pieces, p)).length);}console.log(out.join("\n"));
TypeScriptmain.ts · 56 行
import { readFileSync } from "node:fs";type Cell = { r: number; c: number };const lines: string[] = readFileSync(0, "utf8").split("\n");// 같은 가로줄이나 세로줄에 있는가const straight = (a: Cell, b: Cell): boolean => a.r === b.r || a.c === b.c;// 같은 대각선에 있는가const diagonal = (a: Cell, b: Cell): boolean => Math.abs(a.r - b.r) === Math.abs(a.c - b.c);// a와 b 사이(양 끝은 빼고)에 말이 하나도 없는가function clear(board: string[], a: Cell, b: Cell): boolean { const dr = Math.sign(b.r - a.r), dc = Math.sign(b.c - a.c); let r = a.r + dr, c = a.c + dc; while (r !== b.r || c !== b.c) { if (board[r][c] !== ".") return false; r += dr; c += dc; } return true;}// a 칸에 있는 말이 b 칸을 공격하는가function attacks(board: string[], a: Cell, b: Cell): boolean { const dr = Math.abs(a.r - b.r), dc = Math.abs(a.c - b.c); switch (board[a.r][a.c]) { case "N": return (dr === 1 && dc === 2) || (dr === 2 && dc === 1); case "K": return Math.max(dr, dc) === 1; case "R": return straight(a, b) && clear(board, a, b); case "B": return diagonal(a, b) && clear(board, a, b); default: return (straight(a, b) || diagonal(a, b)) && clear(board, a, b); }}// 다른 어떤 말도 target을 공격하지 않는가const isSafe = (board: string[], pieces: Cell[], target: Cell): boolean => !pieces.some((p) => p !== target && attacks(board, p, target));const t = Number(lines[0]);let at = 1;const out: number[] = [];for (let k = 0; k < t; k++) { const [h, w] = lines[at].trim().split(" ").map(Number); const board = lines.slice(at + 1, at + 1 + h).map((s) => s.trim()); at += 1 + h; const pieces: Cell[] = []; for (let r = 0; r < h; r++) { for (let c = 0; c < w; c++) { if (board[r][c] !== ".") pieces.push({ r, c }); } } out.push(pieces.filter((p) => isSafe(board, pieces, p)).length);}console.log(out.join("\n"));
JavaMain.java · 71 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.List;public class Main { record Cell(int r, int c) {} static String[] board; // 같은 가로줄이나 세로줄에 있는가 static boolean straight(Cell a, Cell b) { return a.r() == b.r() || a.c() == b.c(); } // 같은 대각선에 있는가 static boolean diagonal(Cell a, Cell b) { return Math.abs(a.r() - b.r()) == Math.abs(a.c() - b.c()); } // a와 b 사이(양 끝은 빼고)에 말이 하나도 없는가 static boolean clear(Cell a, Cell b) { int dr = Integer.signum(b.r() - a.r()), dc = Integer.signum(b.c() - a.c()); int r = a.r() + dr, c = a.c() + dc; while (r != b.r() || c != b.c()) { if (board[r].charAt(c) != '.') return false; r += dr; c += dc; } return true; } // a 칸에 있는 말이 b 칸을 공격하는가 static boolean attacks(Cell a, Cell b) { int dr = Math.abs(a.r() - b.r()), dc = Math.abs(a.c() - b.c()); return switch (board[a.r()].charAt(a.c())) { case 'N' -> (dr == 1 && dc == 2) || (dr == 2 && dc == 1); case 'K' -> Math.max(dr, dc) == 1; case 'R' -> straight(a, b) && clear(a, b); case 'B' -> diagonal(a, b) && clear(a, b); default -> (straight(a, b) || diagonal(a, b)) && clear(a, b); }; } // 다른 어떤 말도 target을 공격하지 않는가 static boolean isSafe(List<Cell> pieces, Cell target) { return pieces.stream().noneMatch(p -> !p.equals(target) && attacks(p, target)); } public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); int t = Integer.parseInt(in.readLine().trim()); StringBuilder out = new StringBuilder(); for (int k = 0; k < t; k++) { String[] hw = in.readLine().trim().split(" "); int h = Integer.parseInt(hw[0]), w = Integer.parseInt(hw[1]); board = new String[h]; List<Cell> pieces = new ArrayList<>(); for (int r = 0; r < h; r++) { board[r] = in.readLine().trim(); for (int c = 0; c < w; c++) { if (board[r].charAt(c) != '.') pieces.add(new Cell(r, c)); } } long safe = pieces.stream().filter(p -> isSafe(pieces, p)).count(); out.append(safe).append('\n'); } System.out.print(out); }}
C#Main.cs · 70 行
using System;using System.Collections.Generic;using System.Linq;using System.Text;class Program{ static string[] board; // 같은 가로줄이나 세로줄에 있는가 static bool Straight((int r, int c) a, (int r, int c) b) => a.r == b.r || a.c == b.c; // 같은 대각선에 있는가 static bool Diagonal((int r, int c) a, (int r, int c) b) => Math.Abs(a.r - b.r) == Math.Abs(a.c - b.c); // a와 b 사이(양 끝은 빼고)에 말이 하나도 없는가 static bool Clear((int r, int c) a, (int r, int c) b) { int dr = Math.Sign(b.r - a.r), dc = Math.Sign(b.c - a.c); int r = a.r + dr, c = a.c + dc; while (r != b.r || c != b.c) { if (board[r][c] != '.') return false; r += dr; c += dc; } return true; } // a 칸에 있는 말이 b 칸을 공격하는가 static bool Attacks((int r, int c) a, (int r, int c) b) { int dr = Math.Abs(a.r - b.r), dc = Math.Abs(a.c - b.c); switch (board[a.r][a.c]) { case 'N': return (dr == 1 && dc == 2) || (dr == 2 && dc == 1); case 'K': return Math.Max(dr, dc) == 1; case 'R': return Straight(a, b) && Clear(a, b); case 'B': return Diagonal(a, b) && Clear(a, b); default: return (Straight(a, b) || Diagonal(a, b)) && Clear(a, b); } } // 다른 어떤 말도 target을 공격하지 않는가 static bool IsSafe(List<(int r, int c)> pieces, (int r, int c) target) => !pieces.Any(p => p != target && Attacks(p, target)); static void Main() { int t = int.Parse(Console.ReadLine().Trim()); var output = new StringBuilder(); for (int k = 0; k < t; k++) { int[] hw = Console.ReadLine().Trim().Split(' ').Select(int.Parse).ToArray(); int h = hw[0], w = hw[1]; board = new string[h]; var pieces = new List<(int r, int c)>(); for (int r = 0; r < h; r++) { board[r] = Console.ReadLine().Trim(); for (int c = 0; c < w; c++) { if (board[r][c] != '.') pieces.Add((r, c)); } } output.Append(pieces.Count(p => IsSafe(pieces, p))).Append('\n'); } Console.Write(output); }}
C++main.cpp · 67 行
#include <algorithm>#include <cstdlib>#include <iostream>#include <string>#include <vector>struct Cell { int r, c; bool operator==(const Cell& o) const { return r == o.r && c == o.c; }};using Board = std::vector<std::string>;// 같은 가로줄이나 세로줄에 있는가bool straight(Cell a, Cell b) { return a.r == b.r || a.c == b.c; }// 같은 대각선에 있는가bool diagonal(Cell a, Cell b) { return std::abs(a.r - b.r) == std::abs(a.c - b.c); }int sign(int x) { return (x > 0) - (x < 0); }// a와 b 사이(양 끝은 빼고)에 말이 하나도 없는가bool clear(const Board& board, Cell a, Cell b) { const int dr = sign(b.r - a.r), dc = sign(b.c - a.c); for (int r = a.r + dr, c = a.c + dc; r != b.r || c != b.c; r += dr, c += dc) { if (board[r][c] != '.') return false; } return true;}// a 칸에 있는 말이 b 칸을 공격하는가bool attacks(const Board& board, Cell a, Cell b) { const int dr = std::abs(a.r - b.r), dc = std::abs(a.c - b.c); switch (board[a.r][a.c]) { case 'N': return (dr == 1 && dc == 2) || (dr == 2 && dc == 1); case 'K': return std::max(dr, dc) == 1; case 'R': return straight(a, b) && clear(board, a, b); case 'B': return diagonal(a, b) && clear(board, a, b); default: return (straight(a, b) || diagonal(a, b)) && clear(board, a, b); }}// 다른 어떤 말도 target을 공격하지 않는가bool isSafe(const Board& board, const std::vector<Cell>& pieces, Cell target) { return std::none_of(pieces.begin(), pieces.end(), [&](Cell p) { return !(p == target) && attacks(board, p, target); });}int main() { int t; std::cin >> t; while (t-- > 0) { int h, w; std::cin >> h >> w; Board board(h); std::vector<Cell> pieces; for (int r = 0; r < h; r++) { std::cin >> board[r]; for (int c = 0; c < w; c++) { if (board[r][c] != '.') pieces.push_back({r, c}); } } std::cout << std::count_if(pieces.begin(), pieces.end(), [&](Cell p) { return isSafe(board, pieces, p); }) << '\n'; }}
Cmain.c · 65 行
#include <stdbool.h>#include <stdio.h>#include <stdlib.h>typedef struct { int r, c; } Cell;static char board[12][13];/* 같은 가로줄이나 세로줄에 있는가 */static bool straight(Cell a, Cell b) { return a.r == b.r || a.c == b.c; }/* 같은 대각선에 있는가 */static bool diagonal(Cell a, Cell b) { return abs(a.r - b.r) == abs(a.c - b.c); }static int sign(int x) { return (x > 0) - (x < 0); }/* a와 b 사이(양 끝은 빼고)에 말이 하나도 없는가 */static bool clear(Cell a, Cell b) { int dr = sign(b.r - a.r), dc = sign(b.c - a.c); for (int r = a.r + dr, c = a.c + dc; r != b.r || c != b.c; r += dr, c += dc) { if (board[r][c] != '.') return false; } return true;}/* a 칸에 있는 말이 b 칸을 공격하는가 */static bool attacks(Cell a, Cell b) { int dr = abs(a.r - b.r), dc = abs(a.c - b.c); switch (board[a.r][a.c]) { case 'N': return (dr == 1 && dc == 2) || (dr == 2 && dc == 1); case 'K': return (dr > dc ? dr : dc) == 1; case 'R': return straight(a, b) && clear(a, b); case 'B': return diagonal(a, b) && clear(a, b); default: return (straight(a, b) || diagonal(a, b)) && clear(a, b); }}/* 다른 어떤 말도 pieces[target]을 공격하지 않는가 */static bool is_safe(const Cell *pieces, int count, int target) { for (int i = 0; i < count; i++) { if (i != target && attacks(pieces[i], pieces[target])) return false; } return true;}int main(void) { int t; scanf("%d", &t); while (t-- > 0) { int h, w, count = 0, safe = 0; Cell pieces[40]; scanf("%d %d", &h, &w); for (int r = 0; r < h; r++) { scanf("%12s", board[r]); for (int c = 0; c < w; c++) { if (board[r][c] != '.') pieces[count++] = (Cell){r, c}; } } for (int i = 0; i < count; i++) { if (is_safe(pieces, count, i)) safe++; } printf("%d\n", safe); } return 0;}
Craykonmain.cray · 111 行
판 = [];// 말이 있는 칸의 줄 번호와 칸 번호줄들 = [];칸들 = [];부호(x){ もし (x > 0) { 返す 1; } もし (x < 0) { 返す -1; } 返す 0;}// 같은 가로줄이나 세로줄에 있는가곧은줄인가(a, b){ 返す 줄들[a] == 줄들[b] || 칸들[a] == 칸들[b];}// 같은 대각선에 있는가대각선인가(a, b){ 返す 数学.絶対値(줄들[a] - 줄들[b]) == 数学.絶対値(칸들[a] - 칸들[b]);}// a와 b 사이(양 끝은 빼고)에 말이 하나도 없는가비었나(a, b){ dr = 부호(줄들[b] - 줄들[a]); dc = 부호(칸들[b] - 칸들[a]); r = 줄들[a] + dr; c = 칸들[a] + dc; 間 (r != 줄들[b] || c != 칸들[b]) { もし (판[r][c] != ".") { 返す 偽; } r += dr; c += dc; } 返す 真;}// a번 말이 b번 말을 공격하는가공격하나(a, b){ dr = 数学.絶対値(줄들[a] - 줄들[b]); dc = 数学.絶対値(칸들[a] - 칸들[b]); 選択 (판[줄들[a]][칸들[a]]) { 場合 "N": 返す dr == 1 && dc == 2 || dr == 2 && dc == 1; 場合 "K": 返す 数学.大きい方(dr, dc) == 1; 場合 "R": 返す 곧은줄인가(a, b) && 비었나(a, b); 場合 "B": 返す 대각선인가(a, b) && 비었나(a, b); } 返す (곧은줄인가(a, b) || 대각선인가(a, b)) && 비었나(a, b);}// 다른 어떤 말도 b번 말을 공격하지 않는가안전한가(b){ 繰り返す (i = 0; i < 줄들.個数; i++) { もし (i != b && 공격하나(i, b)) { 返す 偽; } } 返す 真;}t = コンソール.行読み().数に();繰り返す (k = 0; k < t; k++){ hw = コンソール.数読み(); 판 = []; 줄들 = []; 칸들 = []; 繰り返す (r = 0; r < hw[0]; r++) { 줄 = コンソール.行読み().空白除去(); 판.追加(줄); 繰り返す (c = 0; c < hw[1]; c++) { もし (줄[c] != ".") { 줄들.追加(r); 칸들.追加(c); } } } 안전 = 0; 繰り返す (i = 0; i < 줄들.個数; i++) { もし (안전한가(i)) { 안전++; } } コンソール.書く(안전);}