[코드트리 - L13] 25년 상반기 오후 1번 미생물 연구

2026. 4. 9. 21:58·알고리즘

 

 

 

 

코딩테스트 기출 문제 제출: 미생물 연구 | 코드트리

코딩테스트 기출 문제 미생물 연구의 풀이를 제출하고 즉시 채점 결과를 확인하세요. 실시간 피드백으로 코딩 실력을 향상시킵니다.

www.codetree.ai

 

 

문제풀이

1. 미생물 투입

  • 이미 해당 위치에 다른 미생물이 있다면 새로 투입된 미생물만 남는다.
  • 기존에 있던 미생물 A가 새로 투입된 B에 먹혀, 2 이상의 그룹으로 나뉜다면 A는 모두 사라진다.

2. 배양용기 이동

  • 가장 넓은 영역을 가진 미생물 선택한다. 둘 이상이면, 먼저 투입된 미생물을 선택한다.
  • 기존 형태를 유지한 채로 다른 미생물이랑 겹치지 않게 (최대한 작은 x , 최대한 작은 y)로 이동한다.
  • 어디도 둘 수 없다면 사라진다.

3. 실험 결과

  • 모든 인접한 무리쌍을 확인한다. 이 때, 두 무리 A와 B가 맞닿은 면이 둘 이상이더라도 (A, B) 쌍은 한 번만 확인합니다.
  • 확인하는 두 무리가 A, B라면 (미생물 A의 영역의 넓이) × (미생물 B의 영역의 넓이) 만큼의 성과를 얻는다.
  • 모든 쌍의 성과를 더한 것이 정답이다.

 

1. 미생물 투입

for (int i = sx; i < ex; i++) {
	for (int j = sy; j < ey; j++) {
		map[i][j] = t + 1;
	}
}

 

 

2. 투입 이후 2개 이상의 그룹으로 나뉘는 지 DFS로 확인

for (int i = 1; i <= t; i++) {
	if (isSplit(i)) {
		remove(i);
	}
}

private static boolean isSplit(int target) {

	boolean[][] visited = new boolean[N][N];
	int cnt = 0;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			if (!visited[i][j] && map[i][j] == target) {
				dfs(i, j, target, visited);
				cnt++;

				if (cnt >= 2)
					return true;
			}
		}
	}
	return false;
}

static void dfs(int x, int y, int target, boolean[][] visited) {

	visited[x][y] = true;

	for (int d = 0; d < 4; d++) {
		int nx = x + dx[d];
		int ny = y + dy[d];

		if (nx < 0 || ny < 0 || nx >= N || ny >= N)
			continue;

		if (!visited[nx][ny] && target == map[nx][ny]) {
			dfs(nx, ny, target, visited);
		}
	}
}

 

 

3. 배양 용기를 이동한다.

  • 배양용기를 이동시키기 전, 그룹끼리 정렬하기 위해서 그룹을 형성해준다.
private static List<Group> getGroups() {

	boolean[][] visited = new boolean[N][N];
	List<Group> groups = new ArrayList<>();

	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			if (map[i][j] != 0 && !visited[i][j]) {
				groups.add(bfs(i, j, visited));
			}
		}
	}

	return groups;
}

 

  • 그룹을 넓이 순, 먼저 투입된 순으로 정렬한다.
  • 그룹 내 가장 작은 위치인 (minX, minY)로 거리를 이동시킨다.
    • ex) (2, 3) => (0, 0) 으로 이동시켜야 한다면, 모든 다른 좌표들을 (2, 3)만큼 빼주면 모양 유지한 채 이동할 수 있다.
static void moveGroups() {
		
        List<Group> groups = getGroups();

		// 그룹 정렬
		groups.sort((a, b) -> {
			if (a.size != b.size)
				return b.size - a.size; // 큰 넓이 순
			return a.id - b.id; // 먼저 투입된 것부터
		});

		int[][] newMap = new int[N][N];

		for (Group g : groups) {

			boolean placed = false;

			// 위치 탐색
			for (int x = 0; x < N && !placed; x++) {
				for (int y = 0; y < N && !placed; y++) {

					boolean canPlace = true;

					for (int[] cell : g.cells) {

						int nx = x + (cell[0] - g.minX);
						int ny = y + (cell[1] - g.minY);

						// 범위 넘거나 이미 있다면 이동 불가
						if (nx < 0 || ny < 0 || nx >= N || ny >= N) {
							canPlace = false;
							break;
						}

						if (newMap[nx][ny] != 0) {
							canPlace = false;
							break;
						}

					}

					if (canPlace) {
						for (int[] cell : g.cells) {
							int nx = x + (cell[0] - g.minX);
							int ny = y + (cell[1] - g.minY);
							newMap[nx][ny] = g.id;
						}
						placed = true;
					}
				}
			}
		}

		map = newMap;
	}

 

 

4. 실험 결과 기록

  • 이 부분은 좀 감이 안잡혀서 AI에게 물어봤다. ㅎㅎ
  • 아래와 오른쪽 부분에 다른 그룹이 있는 지 체크한 후, (A 그룹 ID, B 그룹 ID) 로 쌍들을 Set에 넣는다.
  • 아랫부분과 오른쪽 부분만 체크하는 이유는 2번 그룹의 오른쪽에서 (2, 3)의 쌍이 있다면, 3번 그룹의 왼쪽에서도 (3, 2) 쌍이 있기 때문에 중복된다.
static void result() {

		// 그룹별 넓이 계산
		Map<Integer, Integer> sizeMap = new HashMap<>();

		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if (map[i][j] != 0) {
					sizeMap.put(map[i][j], sizeMap.getOrDefault(map[i][j], 0) + 1);
				}
			}
		}

		int result = 0;
		Set<String> seen = new HashSet<>();

		// 인접 체크
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if (map[i][j] == 0)
					continue;

				int cur = map[i][j];

				// 오른쪽이 0 아닌 다른 값일 때
				if (j + 1 < N && map[i][j + 1] != 0 && map[i][j + 1] != cur) {
					int next = map[i][j + 1];

					int a = Math.min(cur, next);
					int b = Math.max(cur, next);

					String key = a + "," + b;

					if (!seen.contains(key)) {
						result += sizeMap.get(a) * sizeMap.get(b);
						seen.add(key);
					}
				}

				// 아래
				if (i + 1 < N && map[i + 1][j] != 0 && map[i + 1][j] != cur) {
					int next = map[i + 1][j];

					int a = Math.min(cur, next);
					int b = Math.max(cur, next);

					String key = a + "," + b;

					if (!seen.contains(key)) {
						result += sizeMap.get(a) * sizeMap.get(b);
						seen.add(key);
					}

				}

			}

		}
		System.out.println(result);
	}

 

 


  •  

 

전체코드

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;

public class Main {

	static class Group {
		int id;
		int size;
		int minX, minY;
		List<int[]> cells = new ArrayList<>();
	}

	static int N, Q;
	static int[][] map;
	static int[] dx = { -1, 1, 0, 0 };
	static int[] dy = { 0, 0, -1, 1 };

	public static void main(String[] args) throws Exception {

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String[] input = br.readLine().split(" ");

		N = Integer.parseInt(input[0]);
		Q = Integer.parseInt(input[1]);

		map = new int[N][N];
		for (int t = 0; t < Q; t++) {

			input = br.readLine().split(" ");
			int sx = Integer.parseInt(input[0]);
			int sy = Integer.parseInt(input[1]);
			int ex = Integer.parseInt(input[2]);
			int ey = Integer.parseInt(input[3]);

			// 1. 미생물 투입
			for (int i = sx; i < ex; i++) {
				for (int j = sy; j < ey; j++) {
					map[i][j] = t + 1;
				}
			}
			// 2. 둘 이상으로 나뉘는 지 확인
			for (int i = 1; i <= t; i++) {
				if (isSplit(i)) {
					remove(i);
				}
			}

			// 3. 배양 용기 이동: 가장 넓은 미생물무터 가장 작은 x, 가장 작은 y, 둘 공간이 없으면 사라짐
			moveGroups();

			// 4. 실험 성과: 인접한 무리별 넓이 쌍 더하기
			result();

		}

	}

	private static void result() {

		// 그룹별 넓이 계산
		Map<Integer, Integer> sizeMap = new HashMap<>();

		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if (map[i][j] != 0) {
					sizeMap.put(map[i][j], sizeMap.getOrDefault(map[i][j], 0) + 1);
				}
			}
		}

		int result = 0;
		Set<String> seen = new HashSet<>();

		// 인접 체크
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if (map[i][j] == 0)
					continue;

				int cur = map[i][j];

				// 오른쪽이 0 아닌 다른 값일 때
				if (j + 1 < N && map[i][j + 1] != 0 && map[i][j + 1] != cur) {
					int next = map[i][j + 1];

					int a = Math.min(cur, next);
					int b = Math.max(cur, next);

					String key = a + "," + b;

					if (!seen.contains(key)) {
						result += sizeMap.get(a) * sizeMap.get(b);
						seen.add(key);
					}
				}

				// 아래
				if (i + 1 < N && map[i + 1][j] != 0 && map[i + 1][j] != cur) {
					int next = map[i + 1][j];

					int a = Math.min(cur, next);
					int b = Math.max(cur, next);

					String key = a + "," + b;

					if (!seen.contains(key)) {
						result += sizeMap.get(a) * sizeMap.get(b);
						seen.add(key);
					}

				}

			}

		}
		System.out.println(result);
	}

	private static void moveGroups() {
		List<Group> groups = getGroups();

		// 그룹 정렬
		groups.sort((a, b) -> {
			if (a.size != b.size)
				return b.size - a.size; // 큰 넓이 순
			return a.id - b.id; // 먼저 투입된 것부터
		});

		int[][] newMap = new int[N][N];

		for (Group g : groups) {

			boolean placed = false;

			// 위치 탐색
			for (int x = 0; x < N && !placed; x++) {
				for (int y = 0; y < N && !placed; y++) {

					boolean canPlace = true;

					for (int[] cell : g.cells) {

						int nx = x + (cell[0] - g.minX);
						int ny = y + (cell[1] - g.minY);

						// 범위 넘거나 이미 있다면 이동 불가
						if (nx < 0 || ny < 0 || nx >= N || ny >= N) {
							canPlace = false;
							break;
						}

						if (newMap[nx][ny] != 0) {
							canPlace = false;
							break;
						}

					}

					if (canPlace) {
						for (int[] cell : g.cells) {
							int nx = x + (cell[0] - g.minX);
							int ny = y + (cell[1] - g.minY);
							newMap[nx][ny] = g.id;
						}
						placed = true;
					}
				}
			}
		}

		map = newMap;
	}

	private static List<Group> getGroups() {

		boolean[][] visited = new boolean[N][N];
		List<Group> groups = new ArrayList<>();

		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if (map[i][j] != 0 && !visited[i][j]) {
					groups.add(bfs(i, j, visited));
				}
			}
		}

		return groups;
	}

	private static Group bfs(int x, int y, boolean[][] visited) {

		Queue<int[]> q = new ArrayDeque<>();
		q.add(new int[] { x, y });
		visited[x][y] = true;

		Group g = new Group();
		g.id = map[x][y];
		g.size = 0;
		g.minX = x;
		g.minY = y;

		while (!q.isEmpty()) {

			int[] cur = q.poll();
			x = cur[0];
			y = cur[1];

			g.cells.add(new int[] { x, y });
			g.size++;

			if (x < g.minX || (x == g.minX && y < g.minY)) { // x가 작거나, 같다면 y가 더 작은 것
				g.minX = x;
				g.minY = y;
			}

			for (int d = 0; d < 4; d++) {
				int nx = x + dx[d];
				int ny = y + dy[d];

				if (nx < 0 || ny < 0 || nx >= N || ny >= N)
					continue;

				if (!visited[nx][ny] && g.id == map[nx][ny]) {
					visited[nx][ny] = true;
					q.add(new int[] { nx, ny });
				}
			}

		}

		return g;
	}

	static void remove(int target) {
		for (int x = 0; x < N; x++) {
			for (int y = 0; y < N; y++) {
				if (map[x][y] == target) {
					map[x][y] = 0;
				}
			}
		}
	}

	private static boolean isSplit(int target) {

		boolean[][] visited = new boolean[N][N];
		int cnt = 0;
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if (!visited[i][j] && map[i][j] == target) {
					dfs(i, j, target, visited);
					cnt++;

					if (cnt >= 2)
						return true;
				}
			}
		}

		return false;
	}

	private static void dfs(int x, int y, int target, boolean[][] visited) {

		visited[x][y] = true;

		for (int d = 0; d < 4; d++) {
			int nx = x + dx[d];
			int ny = y + dy[d];

			if (nx < 0 || ny < 0 || nx >= N || ny >= N)
				continue;

			if (!visited[nx][ny] && target == map[nx][ny]) {
				dfs(nx, ny, target, visited);
			}
		}

	}

}
저작자표시 비영리 변경금지 (새창열림)

'알고리즘' 카테고리의 다른 글

[코드트리 - L15] 23년 상반기 오전 1번 포탑 부수기  (0) 2026.04.10
[코드트리 - L12] 25년 상반기 오전 1번 민트초코우유  (1) 2026.04.08
[코드트리 - L13] 25년 하반기 오후 1번 AI 로봇청소기  (0) 2026.04.07
[코드트리 - L12] 25년 하반기 오전 1번 문제 - 택배 하차  (0) 2026.04.07
[알고리즘] 시간 복잡도 & 공간 복잡도  (0) 2026.04.05
'알고리즘' 카테고리의 다른 글
  • [코드트리 - L15] 23년 상반기 오전 1번 포탑 부수기
  • [코드트리 - L12] 25년 상반기 오전 1번 민트초코우유
  • [코드트리 - L13] 25년 하반기 오후 1번 AI 로봇청소기
  • [코드트리 - L12] 25년 하반기 오전 1번 문제 - 택배 하차
수웅
수웅
  • 수웅
    야금야금 공부
    수웅
  • 전체
    오늘
    어제
    • 분류 전체보기 (106) N
      • 코딩 (5)
      • 알고리즘 (59)
      • CS (19) N
      • 취준 (1)
      • 안드로이드 (17)
        • 코틀린 (6)
        • 정리 (10)
        • 프로젝트 (0)
      • Error (1)
      • Git (2)
      • 기타 (2)
  • 블로그 메뉴

    • 홈
    • 글쓰기
  • 링크

  • 공지사항

  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
수웅
[코드트리 - L13] 25년 상반기 오후 1번 미생물 연구
상단으로

티스토리툴바