🧫 문제 분석
✔️ 출처
📖 문제


랜덤 문제를 돌리다가 나왔다. 그래프 탐색 냄새가 솔솔 난다. BFS를 사용하면 될것 같다.
1. 한 나라의 좌우 상하로 인접한 다른 나라와의 인구차를 M명이라 했을 때 L <= M <= R 을 만족해야한다.
2. 조건을 만족한 나라들과는 연합을 하여 국경선을 하루 연다.
3. 인구가 이동하며 각 나라의 인구수는 (연합 인구수 / 연합한 나라 개수) 가 되며 소수점은 버린다.
4. 이동후 연합 해지 및 모든 국경선을 닫는다.
5. 인구 이동이 없을때까지 반복한다.
6. 인구 이동이 며칠동안 발생했는지가 문제이다.
내가 생각한 방법
1. 우선 연합가능한 나라들을 찾고, 그 나라에 연합 번호를 매기며 각 인구수를 더해놓는다.
2. 연합은 1개 이상 생길 수 있다.
3. 만약 1번 연합이면 1번 연합 나라들의 수를 저장했다가 map에 인구 이동이 된 나라들의 수를 {연합번호 : 인구수 }로 저장한다.
4. 인구 이동을 한 날을 하루 추가하고, 인구 이동이 없을때까지 반복한다.
예제 4번을 예시로 들면
예제 출력은 2이다.

이때 인구 이동 후 1번 연합나라들의 인구수는 142 / 7 = 20명 이다.

인구 이동 후 연합 가능한 나라들을 살펴보니 또 있는 것을 알 수 있다.
또 한번 연합 번호를 매기고 인구 이동을 한다.
50 / 3 = 16이 되므로 1 연합의 3나라들은 16명이 된다.
이 후는 더이상 인구 이동이 불가능하므로 총 2일동안 인구 이동이 발생했다.
🔅 문제 풀이
import java.io.*;
import java.util.*;
public class Main {
static int[] dxarr = {0,0,1,-1};
static int[] dyarr = {1,-1,0,0};
static int[][] union; // 연합 그룹 번호도 마킹
static HashMap<Integer, Integer> map = new HashMap<>();
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int L = Integer.parseInt(st.nextToken());
int R = Integer.parseInt(st.nextToken());
int[][] lands = new int[n][n];
union = new int[n][n];
int answer = 0;
// 땅 배열 값 초기화
for (int i = 0; i < n; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < n; j++) {
lands[i][j] = Integer.parseInt(st.nextToken());
}
}
int maxUnionNum = bfs(lands, L, R);
while(maxUnionNum > 0) {
for (int i = 0; i < lands.length; i++) {
for (int j = 0; j < lands[0].length; j++) {
if (union[i][j] > 0) {
int tmp = map.getOrDefault(union[i][j], 0);
if (tmp > 0) lands[i][j] = tmp;
}
}
}
answer++;
union = new int[n][n]; // 인구 이동이 된 후 다시 연합 가능한 나라 조사를 위해 방문기록 초기화
maxUnionNum = bfs(lands, L, R);
}
bw.write(answer + "\n");
bw.flush();
bw.close();
}
public static int bfs(int[][] land, int L, int R) {
Queue<int[]> q = new LinkedList<>();
int unionNum = 1;
for (int i = 0; i < land.length; i++) {
for (int j = 0; j < land[0].length; j++) {
if (union[i][j] == 0) {
q.add(new int[] {i,j});
union[i][j] = unionNum++;
int count = 0; // 연합이 없는 땅 체크
int popular = 0;
// 연합 땅 번호 새기기
while(!q.isEmpty()) {
int[] landPoint = q.poll();
popular += land[landPoint[0]][landPoint[1]];
for (int k = 0; k < 4; k++) {
int dy = landPoint[0] + dyarr[k]; // 행
int dx = landPoint[1] + dxarr[k]; // 열
if (dx >= 0 && dx < land[0].length && dy >= 0 && dy < land.length) {
if (union[dy][dx] == 0) {
int gap = land[landPoint[0]][landPoint[1]] - land[dy][dx];
if (gap < 0) gap *= -1;
if (gap < L || gap > R) continue; // 인구수 차가 L ~ R이 아니면 다음땅으로
union[dy][dx] = union[i][j];
q.add(new int[] {dy, dx});
count++;
}
}
}
}
if (count == 0){
unionNum--; // 연합이 없다면 연합번호 - 1
union[i][j] = 0; // 연합이 없기에 연합 번호를 없앤다.
}else {
map.put(union[i][j], (popular / (count + 1))); // 연합 번호와 이동 후 인구수를 map에 저장
}
}
}
}
return unionNum - 1;
}
}
ps. 사실 많은 시행착오를 겪었다. 연합 체크를 앞에서 해버려서 무한 루프에 빠지고.. , 연합이 없으면 연합번호를 제거해야하는데 제거하지 않아서 방문했던 모든 나라들에게 연합번호가 부여되서 인구수가 증가해버리는 사태까지 일어났다. 이번에 한 번 풀었으니 경험이 되었을 것이다ㅎㅎ
❗ 오답노트 / 필요한 지식
- 정말정말 많은 시간을 썼다.
- 그래프 탐색 문제에서는 항상 visited를 생각해야한다. 방문한 곳은 더이상 방문하지 않게 한다던가, 아무튼 while(!p.isEmpty)에서 무한루프에 빠지지않게 잘 생각해서 짜자.
- 부족한 그래프 탐색이니 많이 풀어보자.
'baekjoon > Graph_Search' 카테고리의 다른 글
백준 13549번 : 숨바꼭질 3 자바 (0) | 2024.08.08 |
---|---|
백준 1759번 : 암호 만들기 자바 (0) | 2024.07.16 |
백준 실버1 숨바꼭질 1697 (0) | 2024.06.26 |
백준 2667번 : 단지번호붙이기 자바 (1) | 2024.01.16 |
백준 2606번 : 바이러스 자바 (0) | 2024.01.16 |