[백준 silver1] 전쟁 - 전투 (1303)

이민선(Jasmine)·2023년 3월 31일
1

문제

전쟁은 어느덧 전면전이 시작되었다. 결국 전투는 난전이 되었고, 우리 병사와 적국 병사가 섞여 싸우게 되었다. 그러나 당신의 병사들은 흰색 옷을 입고, 적국의 병사들은 파란색 옷을 입었기 때문에 서로가 적인지 아군인지는 구분할 수 있다. 문제는 같은 팀의 병사들은 모이면 모일수록 강해진다는 사실이다.

N명이 뭉쳐있을 때는 N2의 위력을 낼 수 있다. 과연 지금 난전의 상황에서는 누가 승리할 것인가? 단, 같은 팀의 병사들이 대각선으로만 인접한 경우는 뭉쳐 있다고 보지 않는다.

입력

첫째 줄에는 전쟁터의 가로 크기 N, 세로 크기 M(1 ≤ N, M ≤ 100)이 주어진다. 그 다음 두 번째 줄에서 M+1번째 줄에는 각각 (X, Y)에 있는 병사들의 옷색이 띄어쓰기 없이 주어진다. 모든 자리에는 병사가 한 명 있다. B는 파란색, W는 흰색이다. 당신의 병사와 적국의 병사는 한 명 이상 존재한다.

출력

첫 번째 줄에 당신의 병사의 위력의 합과 적국의 병사의 위력의 합을 출력한다.

예제 입력 1

5 5
WBWWW
WWWWW
BBBBB
BBBWW
WWWWW

예제 출력 1

130 65

나의 코드

const input = require("fs")
  .readFileSync("example.txt")
  .toString()
  .trim()
  .split("\n");
const [N, M] = input[0].split(" ");
input.shift();
let board = input.slice().map((r) => r.split(""));

// 한 턴 당 white 또는 black의 인접한 개수를 센다.
let [white, black] = [0, 0];

function dfs(x, y, team) {
// out of range 처리
  if (x < 0 || y < 0 || x >= N || y >= M) {
    return false;
  }
  // 이미 가본 곳은 "X"로 처리하므로 false를 리턴
  if (board[x][y] === "X") return false;

// 어느 팀인지에 따라 어떤 점수를 올릴지가 결정되므로 현재 위치에 따라 팀을 정해야 한다.
  if (!team) {
    team = board[x][y] === "W" ? "myTeam" : "yourTeam";
  }

// 남의 땅을 밟으면 count할 수 없으므로 return false
  if (
    (team === "myTeam" && board[x][y] === "B") ||
    (team === "yourTeam" && board[x][y] === "W")
  ) {
    return false;
  }

// 방문 처리
  board[x][y] = "X";
  // 우리팀이면 white 개수 1만큼 증가, 상대팀이면 black 1만큼 증가
  team === "myTeam" ? white++ : black++;

// 깊이 우선 탐색을 위한 재귀 호출
  dfs(x - 1, y, team);
  dfs(x + 1, y, team);
  dfs(x, y - 1, team);
  dfs(x, y + 1, team);

// 우리팀이면 W를, 상대팀이면 B을 표시하여 배열을 반환
  return team === "myTeam" ? ["W", white ** 2] : ["B", black ** 2];
}

// 우리팀과 상대팀의 총점을 초기화
let [totalW, totalB] = [0, 0];

// 행과 열을 한칸 씩 순회하기 위해 2중 for문 사용
for (let i = 0; i < N; i++) {
  for (let j = 0; j < M; j++) {
  // dfs함수 결과 값 반환 (배열의 형태)
    const res = dfs(i, j);
    // 이미 가본 곳이어서 X 표시인 경우 continue
    if (!res) continue;
  
  // 함수의 반환값을 team과 score로 구조분해 할당
    let [team, score] = res;
    // 우리팀 (white)일 때 총점에 더해줌
    if (team === "W") totalW += score;
    // 상대팀(black)일 때 총점에 더해줌
    else totalB += score;
    
    // white이든 black이든 둘 중에 하나는 양수이므로 다음 턴을 위해 둘 다 0으로 초기화
    white = 0;
    black = 0;
  }
}
console.log(totalW, totalB);

우리 팀인지 상대 팀인지에 따라 점수를 분리해서 매겨야 해서 중간중간 조건문을 잘 설정해주어야 했던 문제였다.
특정 index에서 dfs함수를 호출했을 때 최초로 밟은 땅을 기준으로 매개변수인 team이 myTeam인지 yourTeam인지 결정하도록 했다.
이렇게 팀이 결정되면, 재귀 호출을 할 때에도 우리 팀인데 "B"를 밟거나, 상대 팀인데 "W"를 밟을 때에는 카운트하지 않을 수 있다.
이렇게 지옥의 if문을 모두 통과할 때마다, 방문 처리를 후 dfs 외부에 있는 white와 black 중 현재 팀 색에 부합하는 변수의 값을 1씩 증가시켜준다.

그 이후 재귀 호출을 하고, 인접한 노드를 모두 방문하고 나면 이번 회차의 인접 노드 개수 뿐만 아니라 팀 이름도 함께 배열에 담아 return 한다.

dfs 함수의 반환값이 truthy한 값일 때는 totalW, totalB이라는 변수에 누적해준다.

이 문제는 내가 사랑하는 보드게임인 장미 전쟁과 점수 내는 방식이 동일하다. 문제도 아주 애정이 가는군 (๑•́ u •̀๑)

애정이 가는 것 치고는 좀 바보 같은 실수를 했다 ㅋㅋㅋㅋㅋㅋ
가로 길이와 세로 길이가 각각 N, M이면 행 개수와 열 개수 중 뭐가 N이고 뭐가 M인가?
나는 가로 길이가 행의 개수를 가리킨다고 잘못 이해했다 ㅋㅋㅋㅋㅋㅋ 은근 헷갈렸다고 ㅠㅠ

그렇게 영문도 모르고 틀렸습니다만 계속 뜨다가 문득 가로길이라는 단어와 세로길이라는 단어의 의미를 다시 생각해보았다.
가로 길이는 사각형의 가로 길이이므로 열의 개수와 같고,
세로 길이는 사각형의 세로 길이이므로 행의 개수와 같다.

0개 국어인거 티 내지 말자고 했잖아..
세종대왕님 죄송합니다...
가로 길이와 세로 길이를 주면 사각형을 먼저 떠올려서 헷갈리지 말자 ㅋㅋㅋㅋㅋㅋㅋㅋ

profile
기록에 진심인 개발자 🌿

0개의 댓글