CodingTest/백준
[백준] BOJ 18809 Gaaaaaaaaaarden (골드 1)
브디크리
2024. 4. 22. 22:34
문제
https://www.acmicpc.net/problem/18809
18809번: Gaaaaaaaaaarden
첫째 줄에 정원의 행의 개수와 열의 개수를 나타내는 N(2 ≤ N ≤ 50)과 M(2 ≤ M ≤ 50), 그리고 초록색 배양액의 개수 G(1 ≤ G ≤ 5)와 빨간색 배양액의 개수 R(1 ≤ R ≤ 5)이 한 칸의 빈칸을 사이에 두
www.acmicpc.net
해설
- 해당 문제 해결을 위해서는 조합 + 조합을 구현해야 한다.
- N개의 가능한 토양에서 (G+R : 모든 배양액의 수)개를 선택해야한다. n C (g + r) → 1번 조합
- 선택한 토양에서 어디에 Green을 배치할지 (선택되지 않은 토양은 Red) (g + r) C g → 2번 조합
- 조합은 NextPermutation 알고리즘을 이용하여 메서드로 빼고 2번의 조합을 구할때 하나의 메서드를 재사용한다.
- 각각의 배양액을 다른 큐로 관리하고, 각각을 너비만큼 BFS를 수행한다.
- 너비만큼 수행하면 같은 시간(너비)를 가지는 큐노드만 한번의 탐색에서 수행되므로
같은 시간에 도착한 것을 확인할 수 있다.
주의사항
코드 (시뮬레이션 + 브루트포스)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class BOJ18809 {
static int N, M, G, R;
static int[][] map;
static int max = 0;
static List<Pair> places;
static int[] dx = {-1, 1, 0, 0};
static int[] dy = {0, 0, -1, 1};
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
G = Integer.parseInt(st.nextToken());
R = Integer.parseInt(st.nextToken());
map = new int[N][M];
places = new ArrayList<>();
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < M; j++) {
map[i][j] = Integer.parseInt(st.nextToken());
if(map[i][j] == 2) places.add(new Pair(i, j, 1));
}
}
combinationPlace();
System.out.println(max);
}
/**
* K개의 배양 가능한 토양의 개수에서 G + R개의 토양 선택 (조합)
* G + R개의 선택된 토양에서 G개의 토양에 G, 나머지에 R 두기 (조합)
*/
//토양뽑기
private static void combinationPlace() {
int[] idxs = new int[places.size()];
//places에서 조합으로 뽑아낼 인덱스
for (int i = idxs.length - 1; i >= idxs.length - (G + R); i--) {
idxs[i] = 1;
}
do {
combinationColor(idxs);
} while (nextPermutation(idxs));
}
// 뽑은 토양에서 다시 배치할 색 뽑기
private static void combinationColor(int[] idxs) {
int[] colors = new int[G + R];
for (int i = colors.length - 1; i >= R; i--) {
colors[i] = 1;
}
do {
simulation(idxs, colors);
} while (nextPermutation(colors));
}
private static void simulation(int[] idxs, int[] colors) {
Queue<Pair> reds = new ArrayDeque<>();
Queue<Pair> greens = new ArrayDeque<>();
int idx = 0;
int[][][] isv = new int[2][N][M];
for (int i = 0; i < idxs.length; i++) {
if (idxs[i] == 1) {
Pair pair = places.get(i);
if (colors[idx++] == 0) {
reds.offer(pair);
isv[1][pair.x][pair.y] = 1;
} else {
greens.offer(pair);
isv[0][pair.x][pair.y] = 1;
}
}
}
bfs(reds, greens, isv);
}
private static void bfs(Queue<Pair> reds, Queue<Pair> greens, int[][][] isv) {
int cnt = 0;
while (!reds.isEmpty() || !greens.isEmpty()) {
int size1 = greens.size();
while (size1-- > 0) {
cnt = go(greens, isv, cnt, 0);
}
int size2 = reds.size();
while (size2-- > 0) {
cnt = go(reds, isv, cnt, 1);
}
}
max = Math.max(max, cnt);
}
public static int go(Queue<Pair> queue, int[][][] isv, int cnt, int flag){
Pair p = queue.poll();
/**
* 4방 탐색에서 처리하지 않는 이유
* 로직
* 1. green이 너비만큼 먼저 이동
* 2. red가 너비만큼 이동
* 3. 조건 : 너비 (사실상 시간)이 동일할 때 꽃이 핀다.
* 4. 위 조건을 만족하려면 red가 이동할때 green의 너비와 같으면 꽃이 피는 것이다.
* 5. 이때, green입장에서는 더 퍼지면 안되는 노드들이 이미 큐에 들어가있는 상태이다.
* ex) green(2,2)에 2초에 도착 -> 4방탐색하며 큐에 4개를 시간 3과함께 넣는다.
* 현재 green(2,3)에 3초에 도착이라고 표시하고 큐에 넣은 상태
*/
//red일때는 green, green일때는 red를 확인하는 것을 flag^1로 반대 확인
if(isv[flag^1][p.x][p.y] == -1) return cnt;
if(isv[flag^1][p.x][p.y] == p.t) {
cnt++;
isv[0][p.x][p.y] = -1;
isv[1][p.x][p.y] = -1;
return cnt;
}
for (int i = 0; i < 4; i++) {
int nx = p.x + dx[i];
int ny = p.y + dy[i];
if (isValid(nx, ny) || isv[flag][nx][ny] != 0) continue;
if(isv[flag^1][nx][ny] == 0 || isv[flag^1][nx][ny] == p.t + 1){
queue.offer(new Pair(nx, ny, p.t + 1));
isv[flag][nx][ny] = p.t + 1;
}
}
return cnt;
}
private static boolean isValid(int nx, int ny) {
return nx < 0 || ny < 0 || nx >= N || ny >= M || map[nx][ny] == 0;
}
public static boolean nextPermutation(int[] arr){
int i = arr.length - 1;
while (i > 0 && arr[i - 1] >= arr[i]) i--;
if(i == 0) return false;
int dest = i - 1;
int j = arr.length - 1;
while (j > dest && arr[dest] >= arr[j]) j--;
swap(arr, j, dest);
int k = arr.length - 1;
while (i < k) swap(arr, i++, k--);
return true;
}
private static void swap(int[] arr, int j, int dest) {
int temp = arr[j];
arr[j] = arr[dest];
arr[dest] = temp;
}
}
class Pair{
int x, y, t;
public Pair(int x, int y, int t) {
this.x = x;
this.y = y;
this.t = t;
}
}
결론
해설이 정말 간단하지만, 조합을 2번 수행해야 하고 BFS할 때 고려해야할 사항이 많았다.
특히 처음에 문제를 잘못 생각해서 배양액이 이미 있는 곳도 갈 수 있다고 생각해서 시간을 날렸고,
배양액 1개당 꽃이 1개만 필 수 있는 줄 알아서 배양액이 1개 생성되었을 때
해당 배양액으로 부터 BFS로 퍼져나온 모든 배양액을 추적해서 지워주도록 로직을 작성했었다..
문제를 잘 읽는 것도 중요하지만 이번 문제는 솔직히 설명이 조금은 부족하지 않았나 싶다 ㅠㅠ
하지만 결론적으로는 해결해서 너무 뿌듯했던 문제!!