DFS 완전 정복 - II

이강용·2023년 12월 1일
0

알고리즘 개념

목록 보기
5/14

같은 부류 찾기 유형

  • 연결된 묶음 / 덩어리의 개수는 몇개인가?
  • 가장 큰 덩어리의 크기는 얼마인가?

주요 키워드 : 인정합 위치로 이동, 상하좌우, 가로, 세로, 대각선 이동

  • 주어진 정보를 어떻게 변환할 지
  • 재방문을 방지하는 방법
  • 어느 지점에서 DFS를 시작할 지
  • 어느 방향으로 DFS를 진행할 지

백준 1012

문) 차세대 영농인 한나는 강원도 고랭지에서 유기농 배추를 재배하기로 하였다. 농약을 쓰지 않고 배추를 재배하려면 배추를 해충으로부터 보호하는 것이 중요하기 때문에, 한나는 해충 방지에 효과적인 배추흰지렁이를 구입하기로 결심한다. 이 지렁이는 배추 근처에 서식하며 해충을 잡아 먹음으로써 배추를 보호한다. 특히, 어떤 배추에 배추흰지렁이가 한 마리라도 살고 있으면 이 지렁이는 인접한 다른 배추로 이동할 수 있어, 그 배추들 역시 해충으로부터 보호받을 수 있다. 한 배추의 상하좌우 네 방향에 다른 배추가 위치한 경우에 서로 인접해있는 것이다.

한나가 배추를 재배하는 땅은 고르지 못해서 배추를 군데군데 심어 놓았다. 배추들이 모여있는 곳에는 배추흰지렁이가 한 마리만 있으면 되므로 서로 인접해있는 배추들이 몇 군데에 퍼져있는지 조사하면 총 몇 마리의 지렁이가 필요한지 알 수 있다.


제한사항

  • M, N : 가로, 세로 ( 1 <= N, M <= 50 )
  • K : 배추가 심어져 있는 위치의 개수 ( 1 <= K <= 2500 )

M10
N8
K17

K개의 배추 위치

00
10
11
42
43
45
24
34

나의 풀이

package programmers;

public class Dfs5 {
	
	final static int MAX = 50 + 10;
	static boolean[][] map;
	
	static int[][] DIRS = {{-1,0},{1,0},{0,-1},{0,1}};
	
	public static void dfs(int y, int x) {
		map[y][x] = false;
		
	    for(int[] dir : DIRS) {
	    	int newY = y + dir[0];
	    	int newX = x + dir[1];
	    	if(map[newY][newX]){
	    		dfs(newY, newX);
	    	}
	    }
		
	}
	
	public static int solution(int M, int N, int K, int[][] connections) {
		
		map = new boolean[MAX][MAX];
		
		
		for(int i = 0; i < K; i++) {
			int x = connections[i][0];
			int y = connections[i][1];
			map[y+1] [x+1] = true;
		}
		int answer = 0;
		
		for(int i = 1; i <= N; i++) {
			for(int j = 1; j <= M; j++) {
				if(map[i][j]) {
					answer++;
					dfs(i, j);					
				}
			}
		}
		System.out.println(answer);
		return answer;
	}
	public static void main(String[] args) {
		
		int[][] connections = {{0, 0}, {1, 0}, {1, 1}, {4, 2}, {4, 3}, {4, 5}
		,{2,4},{3,4},{7,4},{8,4},{9,4},{7,5},{8,5},{9,5},{7,6},{8,6},{9,6}};
		solution(10,8,17, connections);
	}

}

정리

  1. 인접한 다른 배추로 이동, 상하좌우 -> DFS/BFS
  2. 서로 연결되었다는 정보를 어떻게 하나의 자료구조로 통합할까?
  • 2차원 배열 vs ArrayList
  1. 이미 방문한 지점을 다시 방문하지 않으려면 어떤 자료구조를 사용해야 할까?
  2. visited 배열을 생략할 수는 없을까?

백준 13565

문) 인제대학교 생화학연구실에 재직중인 석교수는 전류가 침투(percolate) 할 수 있는 섬유 물질을 개발하고 있다. 이 섬유 물질은 2차원 M × N 격자로 표현될 수 있다. 편의상 2차원 격자의 위쪽을 바깥쪽(outer side), 아래쪽을 안쪽(inner side)라고 생각하기로 한다. 또한 각 격자는 검은색 아니면 흰색인데, 검은색은 전류를 차단하는 물질임을 뜻하고 흰색은 전류가 통할 수 있는 물질임을 뜻한다. 전류는 섬유 물질의 가장 바깥쪽 흰색 격자들에 공급되고, 이후에는 상하좌우로 인접한 흰색 격자들로 전달될 수 있다.

김 교수가 개발한 섬유 물질을 나타내는 정보가 2차원 격자 형태로 주어질 때, 바깥쪽에서 흘려 준 전류가 안쪽까지 침투될 수 있는지 아닌지를 판단하는 프로그램을 작성하시오.

Figure 1(a)

Figure 1(b)

예를 들어, Figure 1(a) 에서는 바깥쪽에서 공급된 전류가 안쪽까지 침투하지만, Figure 1(b)에서는 그렇지 못한다.


입력

첫째 줄에는 격자의 크기를 나타내는 M (2 ≤ M ≤ 1,000) 과 N (2 ≤ N ≤ 1,000) 이 주어진다. M줄에 걸쳐서, N개의 0 또는 1 이 공백 없이 주어진다. 0은 전류가 잘 통하는 흰색, 1은 전류가 통하지 않는 검은색 격자임을 뜻한다.


N5
M6

visited 배열 사용


나의 풀이

package programmers;

public class Dfs6 {
	
	final static int MAX = 1000 + 10;
	static boolean[][] map;
	static boolean[][] visited;
	static int[][] DIRS = {{-1,0},{1,0},{0,-1},{0,1}};
	static boolean answer;
	public static boolean dfs(int y, int x, int N) {
		if(y == N) {
			return true;
		}
		visited[y][x] = true;
		
	    for(int[] dir : DIRS) {
	    	int newY = y + dir[0];
	    	int newX = x + dir[1];
	    	if(map[newY][newX] && !visited[newY][newX]){
	    		 if (dfs(newY, newX, N)) {
	                    return true; 
                 }
	    	}
	    }
		return false;
	}
	
	public static void solution(int N, int M, int[][] connections) {
		
		map = new boolean[MAX][MAX];
		visited = new boolean[MAX][MAX];
		
		
		
		// 1. map 정보 반영
		for(int i = 1; i <= N; i++) {
			for(int j = 1; j <= M; j++) {
				map[i][j] = connections[i-1][j-1] == 0;
			}
		}
		
		// 2. dfs 수행
		
		for(int j = 1; j <= M; j++) {
			if(map[1][j]&& dfs(1,j,N)) {
				System.out.println("YES");
				return;
			}
		}
		
		System.out.println("NO");
	}
	public static void main(String[] args) {
		
		int[][] connections = {
				{0, 1, 0, 1, 0, 1},
		        {0, 1, 0, 0, 0, 0},
		        {0, 1, 1, 1, 0, 1},
		        {1, 0, 0, 0, 1, 1},
		        {0, 0, 1, 0, 1, 1}};
		solution(5,6, connections);
	}

}

visited 2차원 배열을 쓰지 않는 방법

package programmers;

public class Dfs6 {
	
	final static int MAX = 1000 + 10;
	static boolean[][] map;
	static int[][] DIRS = {{-1,0},{1,0},{0,-1},{0,1}};
	static boolean answer;
	public static boolean dfs(int y, int x, int N) {
		if(y == N) {
			return true;
		}
		map[y][x] = false;
		
	    for(int[] dir : DIRS) {
	    	int newY = y + dir[0];
	    	int newX = x + dir[1];
	    	if(map[newY][newX]){
	    		 if (dfs(newY, newX, N)) {
	                    return true; 
                 }
	    	}
	    }
		return false;
	}
	
	public static void solution(int N, int M, int[][] connections) {
		
		map = new boolean[MAX][MAX];
		
		// 1. map 정보 반영
		for(int i = 1; i <= N; i++) {
			for(int j = 1; j <= M; j++) {
				map[i][j] = connections[i-1][j-1] == 0;
			}
		}
		
		// 2. dfs 수행
		
		for(int j = 1; j <= M; j++) {
			if(map[1][j]&& dfs(1,j,N)) {
				System.out.println("YES");
				return;
			}
		}
		
		System.out.println("NO");
	}
	public static void main(String[] args) {
		
		int[][] connections = {
				{0, 1, 0, 1, 0, 1},
		        {0, 1, 0, 0, 0, 0},
		        {0, 1, 1, 1, 0, 1},
		        {1, 0, 0, 0, 1, 1},
		        {0, 0, 1, 0, 1, 1}};
		solution(5,6, connections);
	}

}

백준 4963

문) 정사각형으로 이루어져 있는 섬과 바다 지도가 주어진다. 섬의 개수를 세는 프로그램을 작성하시오.

한 정사각형과 가로, 세로 또는 대각선으로 연결되어 있는 사각형은 걸어갈 수 있는 사각형이다.

두 정사각형이 같은 섬에 있으려면, 한 정사각형에서 다른 정사각형으로 걸어서 갈 수 있는 경로가 있어야 한다. 지도는 바다로 둘러싸여 있으며, 지도 밖으로 나갈 수 없다.

  • w, h : 가로, 세로 ( 1 <= w,h <= 50)

keyword

섬의 개수
가로, 세로 또는 대각선으로 연결
한 정사각형에서 다른 정사각형으로 걸어거 갈 수 있는 경로
w5
h4

map, visited 2차원 배열 모두 사용

package programmers;

public class Dfs7 {
	
	final static int MAX = 50 + 10;
	static boolean[][] map;
	static boolean[][] visited;
	static int[] dirY = {-1,-1,0,1,1,1,0,-1};
	static int[] dirX = {0,1,1,1,0,-1,-1,-1};
	
	
	
	public static void dfs(int y, int x) {
		visited[y][x] = true;
		
		for(int i = 0; i < 8; i++) {
			int newY = y + dirY[i];
			int newX = x + dirX[i];
			
			if(map[newY][newX] && !visited[newY][newX]) {
				dfs(newY, newX);
			}
		}
		
	}
	
	public static int solution(int N, int M, int[][] connections) {
		
		int answer = 0;
		
		map = new boolean[MAX][MAX];
		visited = new boolean[MAX][MAX];
		
		for(int i = 1; i <= N; i++) {
			for(int j = 1; j <= M; j++) {
				map[i][j] = connections[i-1][j-1] == 1;
			}
		}
		
		
		for(int i = 1; i <= N; i++) {
			for(int j = 1; j <= M; j++) {
				if(map[i][j] && !visited[i][j]) {
					dfs(i,j);
					answer++;
				}
			}
		}
		
		System.out.println(answer);
		return answer;
		
	}
	public static void main(String[] args) {
		
		int[][] connections = {
				{1, 0, 1, 0, 0},
		        {1, 0, 0, 0, 0},
		        {1, 0, 1, 0, 1},
		        {1, 0, 0, 1, 0}};
		solution(4,5, connections);
	}

}

map 2차원 배열만 사용

package programmers;

public class Dfs7 {
	
	final static int MAX = 50 + 10;
	static boolean[][] map;
	
	static int[] dirY = {-1,-1,0,1,1,1,0,-1};
	static int[] dirX = {0,1,1,1,0,-1,-1,-1};
	
	
	
	public static void dfs(int y, int x) {
		map[y][x] = false;
		
		for(int i = 0; i < 8; i++) {
			int newY = y + dirY[i];
			int newX = x + dirX[i];
			
			if(map[newY][newX]) {
				dfs(newY, newX);
			}
		}
		
	}
	
	public static int solution(int N, int M, int[][] connections) {
		
		int answer = 0;
		
		map = new boolean[MAX][MAX];
		
		
		for(int i = 1; i <= N; i++) {
			for(int j = 1; j <= M; j++) {
				map[i][j] = connections[i-1][j-1] == 1;
			}
		}
		
		
		for(int i = 1; i <= N; i++) {
			for(int j = 1; j <= M; j++) {
				if(map[i][j]) {
					dfs(i,j);
					answer++;
				}
			}
		}
		
		System.out.println(answer);
		return answer;
		
	}
	public static void main(String[] args) {
		
		int[][] connections = {
				{1, 1, 1, 0, 1},
		        {1, 0, 1, 0, 1},
		        {1, 0, 1, 0, 1},
		        {1, 0, 1, 1, 1}};
		solution(4,5, connections);
	}

}

백준 1388

문) 형택이는 건축가이다. 지금 막 형택이는 형택이의 남자 친구 기훈이의 집을 막 완성시켰다. 형택이는 기훈이 방의 바닥 장식을 디자인했고, 이제 몇 개의 나무 판자가 필요한지 궁금해졌다. 나무 판자는 크기1의 너비를 가졌고, 양수의 길이를 가지고 있다. 기훈이 방은 직사각형 모양이고, 방 안에는 평행한 모양의 정사각형으로 나누어져 있다.

이제 -|로 이우어진 바닥 장식 모양이 주어진다. 만약 두 개의 -가 인접해 있고, 같은 행에 있다면, 두 개는 같은 나무 판자이고, 두 개의 |가 인접해 있고, 같은 열에 있다면, 두 개는 같은 나무 판자이다.

기훈이의 방 바닥을 장식하는데 필요한 나무 판자의 개수를 출력하는 프로그램을 작성하시오.

  • N, M : 세로, 가로 (1 <= N,M <= 50)

keyword

두 개의 - 가 인접해 있고, 같은 행에 있다면
두 개의 | 가 인접해 있고, 같은 열에 있다면
N4
M4


map, visited 2차원 배열 사용

package programmers;

public class Dfs8 {
	
	final static int MAX = 50 + 10;
	static char[][] map;
	static boolean[][] visited;
	
	
	
	
	public static void dfs(int y, int x) {
		visited[y][x] = true;
		
		if(map[y][x] == '-' && map[y][x+1] == '-') dfs(y,x+1);
		if(map[y][x] == '|' && map[y+1][x] == '|') dfs(y+1,x);
	}
	
	
	public static int solution(int N, int M, char[][] connections) {
		
		int answer = 0;
		
		map = new char[MAX][MAX];
		visited = new boolean[MAX][MAX];
		
		
		for(int i = 1; i <= N; i++) {
			for(int j = 1; j <= M; j++) {
				map[i][j] = connections[i-1][j-1] ;
			}
		}
		
		for(int i = 1; i <= N; i++) {
			for(int j = 1; j <= M; j++) {
				if(!visited[i][j]) {
					dfs(i,j);
					answer++;
				}
			}
		}
		
		
		System.out.println(answer);
		return answer;
		
	}
	public static void main(String[] args) {
		
		char[][] connections = {
				{'-', '-', '-', '-'},
				{'-', '-', '-', '-'},
				{'-', '-', '-', '-'},
				{'-', '-', '-', '-'}};
		solution(4,4, connections);
	}

}

map 2차원 배열만 사용

package programmers;

public class Dfs8 {
	
	final static int MAX = 50 + 10;
	static char[][] map;
	
	public static void dfs(int y, int x) {
		char cur = map[y][x];
		map[y][x] = 0;
		
		if(cur == '-' && map[y][x+1] == '-') dfs(y,x+1);
		if(cur == '|' && map[y+1][x] == '|') dfs(y+1,x);
	}
	
	
	public static int solution(int N, int M, char[][] connections) {
		
		int answer = 0;
		
		map = new char[MAX][MAX];
		
		
		for(int i = 1; i <= N; i++) {
			for(int j = 1; j <= M; j++) {
				map[i][j] = connections[i-1][j-1] ;
			}
		}
		
		for(int i = 1; i <= N; i++) {
			for(int j = 1; j <= M; j++) {
				if(map[i][j] != 0) {
					dfs(i,j);
					answer++;
				}
			}
		}
		
		
		System.out.println(answer);
		return answer;
		
	}
	public static void main(String[] args) {
		
		char[][] connections = {
				{'-', '-', '-', '-'},
				{'-', '-', '-', '-'},
				{'-', '-', '-', '-'},
				{'-', '-', '-', '-'}};
		solution(4,4, connections);
	}

}

백준 16173

문) ‘쩰리’는 점프하는 것을 좋아하는 젤리다. 단순히 점프하는 것에 지루함을 느낀 ‘쩰리’는 새로운 점프 게임을 해보고 싶어 한다. 새로운 점프 게임의 조건은 다음과 같다.

  1. ‘쩰리’는 가로와 세로의 칸 수가 같은 정사각형의 구역 내부에서만 움직일 수 있다. ‘쩰리’가 정사각형 구역의 외부로 나가는 경우엔 바닥으로 떨어져 즉시 게임에서 패배하게 된다.
  2. ‘쩰리’의 출발점은 항상 정사각형의 가장 왼쪽, 가장 위의 칸이다. 다른 출발점에서는 출발하지 않는다.
  3. ‘쩰리’가 이동 가능한 방향은 오른쪽과 아래 뿐이다. 위쪽과 왼쪽으로는 이동할 수 없다.
  4. ‘쩰리’가 가장 오른쪽, 가장 아래 칸에 도달하는 순간, 그 즉시 ‘쩰리’의 승리로 게임은 종료된다.
  5. ‘쩰리’가 한 번에 이동할 수 있는 칸의 수는, 현재 밟고 있는 칸에 쓰여 있는 수 만큼이다. 칸에 쓰여 있는 수 초과나 그 미만으로 이동할 수 없다.

새로운 게임이 맘에 든 ‘쩰리’는, 계속 게임을 진행해 마침내 최종 단계에 도달했다. 하지만, 게임을 진행하는 구역이 너무 넓어져버린 나머지, 이 게임에서 이길 수 있는지 없는지 가늠할 수 없어졌다. ‘쩰리’는 유능한 프로그래머인 당신에게 주어진 구역에서 승리할 수 있는 지 알아봐 달라고 부탁했다. ‘쩰리’를 도와 주어진 게임 구역에서 끝 점(오른쪽 맨 아래 칸)까지 도달할 수 있는지를 알아보자!

N,M : 세로, 가로 (2 <= N,M <= 3)

출력

  • ‘쩰리’가 끝 점에 도달할 수 있으면 “HaruHaru”(인용부호 없이), 도달할 수 없으면 “Hing” (인용부호 없이)을 한 줄에 출력합니다.

keyword

정사각형의 구역 내부
정사각형의 가장 왼쪽, 가장 위의 칸
오른쪽과 아래
가장 오른쪽, 가장 아래칸
현재 밟고 있는 칸에 쓰여 있는 수 만큼

map, visited 2차원 배열을 사용한 방법

package programmers;

public class Dfs9 {
	
	final static int MAX = 3 + 100 + 10;
	static int[][] map;
	static boolean[][] visited;
	static int[] dirY = {1,0};
	static int[] dirX = {0,1};
	
	public static void dfs(int y, int x, int N) {
		visited[y][x] = true;
		
		if(y == N && x == N) return;
		
		for(int i = 0; i < 2; i++) {
			int newY = y + dirY[i] * map[y][x];
			int newX = x + dirX[i] * map[y][x];
			
			if(!visited[newY][newX]) {
				dfs(newY, newX, N);
			}
		}
	}
	
	
	
	public static int solution(int N, int[][] connections) {
		
		int answer = 0;
		
		map = new int[MAX][MAX];
		visited = new boolean[MAX][MAX];
		
		
		for(int i = 1; i <= N; i++) {
			for(int j = 1; j <= N; j++) {
				map[i][j] = connections[i-1][j-1] ;
			}
		}
		
		dfs(1,1,N);
		
		if(visited[N][N]) {
			System.out.println("HaruHaru");
		}else {
			System.out.println("Hing");
		}
		
		
		
		
		return answer;
		
	}
	public static void main(String[] args) {
		
		int[][] connections = {
				{1, 1, 10},
				{1, 5, 1},
				{2, 2, -1}};
		solution(3, connections);
	}

}

map 2차원 배열만 사용한 방법

package programmers;

public class Dfs9 {
	
	final static int MAX = 3 + 100 + 10;
	static int[][] map;
	
	static int[] dirY = {1,0};
	static int[] dirX = {0,1};
	
	public static void dfs(int y, int x, int N) {
		int value = map[y][x];
		map[y][x] = 0;
		
		if(y == N && x == N) return;
		
		for(int i = 0; i < 2; i++) {
			int newY = y + dirY[i] * value;
			int newX = x + dirX[i] * value;
			
			if(value != 0) {
				dfs(newY, newX, N);
			}
		}
	}
	
	
	
	public static int solution(int N, int[][] connections) {
		
		int answer = 0;
		
		map = new int[MAX][MAX];
		
		
		for(int i = 1; i <= N; i++) {
			for(int j = 1; j <= N; j++) {
				map[i][j] = connections[i-1][j-1] ;
			}
		}
		
		dfs(1,1,N);
		
		if(map[N][N] == 0) {
			System.out.println("HaruHaru");
		}else {
			System.out.println("Hing");
		}
		
		
		
		
		return answer;
		
	}
	public static void main(String[] args) {
		
		int[][] connections = {
				{1, 1, 10},
				{1, 5, 1},
				{2, 2, -1}};
		solution(3, connections);
	}

}
profile
HW + SW = 1

0개의 댓글