[코딩테스트 합격자 되기 스터디] 6주차 - 그래프 (8.11 ~ 8.17)
코딩테스트 합격자 되기 스터디 6주차
강의 보기 -> 해당되는 부분 책 다시 읽기 -> 블로그 정리
강의는 C++로 되어 있지만 개념적인 내용이라 언어 상관 없이 보고 코드는 각 언어에 맞게 바꾸면 된다.
참고 강의 및 책
인프런 : 코딩테스트 합격자 되기 파이썬, C+_+ 저자님 인프런 강의 링크
Youtube : 코딩테스트 합격자 되기 파이썬, C+_+ 저자님 Youtube 링크
종이책 : 코딩테스트 합격자 되기 파이썬 편
종이책 : 코딩테스트 합격자 되기 C++ 편
종이책 : 코딩테스트 합격자 되기 자바스크립트 편
종이책 : 코딩테스트 합격자 되기 자바 편
그래프
그래프의 구현 - 인접 행렬
- 2차원 배열로 표현한다.
- 행과 열의 인덱스로 노드의 값을 나타내고, 배열의 값은 간선의 가중치가 된다.
- 보라색은 가중치다.
- 서울(0), 부산(1)은 노드다.
- 서울에서 부산 방향으로 가는 간선의 가중치는 400이라는 뜻이다.
- 서울(0)에서 부산(1) 방향이니 배열[0][1]에 가중치 400을 대입한다.
- 노드 대비 간선이 적을 경우 메모리 공간 효율이 좋지 않다.
- 노드 100개가 있어도 위 이미지처럼 간선이 한 개만 있는 경우에도 배열[100][100]이 필요하다.
- 노드가 1 -> 2 -> 100 으로 간선이 2개가 있어도 배열[100][100]이 필요하다.
- 즉, 100x100 행렬이 필요하다. (메모리 낭비)
- 특정 노드 사이 간선 존재 여부를 한 번에 알 수 있다.
- 0에서 4까지 간선이 있는가?
- 배열[0][4] 값을 확인하면 한 번에 알 수 있다.
- 가중치가 없는 그래프라면 간선이 있으면 1, 없으면 0으로 표기한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import java.util.Arrays; public class AdjacencyMatrix { // 인접 행렬의 노드 개수 (고정) static final int numNodes = 2; // 인접 행렬 배열 static int[][] adjMatrix = new int[numNodes][numNodes]; // 인접 행렬을 초기화하는 함수 모든 값을 0으로 설정 public static void initializeMatrix() { for (int i = 0; i < numNodes; i++) { Arrays.fill(adjMatrix[i], 0); } } // 간선을 추가하는 함수, u에서 v로 가는 간선의 가중치를 설정 public static void addEdge(int u, int v, int weight) { adjMatrix[u][v] = weight; } // 인접 행렬을 출력하는 함수, 값이 0인 경우 '-'로 출력 public static void printMatrix() { for (int i = 0; i < numNodes; i++) { for (int j = 0; j < numNodes; j++) { if (adjMatrix[i][j] == 0) System.out.print("- "); else System.out.print(adjMatrix[i][j] + " "); } System.out.println(); } } public static void main(String[] args) { // 인접 행렬 초기화 initializeMatrix(); // 엣지 추가: 서울(0)에서 부산(1)으로 가는 엣지, 가중치 400 addEdge(0, 1, 400); // 인접 행렬 출력 printMatrix(); } }
그래프의 구현 - 인접 리스트
- 특정 시작 노드를 기준으로 연결된 노드들을 리스트로 연결하는 방식
- 실제 그래프의 노드 갯수만큼만 추가하므로 메모리 낭비가 없다.
- 특정 노드에 모든 노드가 연결된 경우, 탐색 시 O(N)이 될 수 있다.
- 인접 리스트 표현 방식
- 노드 개수 만큼 배열을 준비한다.
- 각 배열의 인덱스는 시작 노드를 나타낸다.
- 해당 인덱스에 연결된 노드 추가
- 시작 노드1에서 2까지 가는 가중치가 3인 간선을 인접 리스트에 추가한다.
- 2에서 1까지 가는 가중치가 6인 간선을 인접 리스트에 추가한다.
- 2에서 3까지 가는 가중치가 5인 간선을 인접 리스트에 추가한다.
- 3에서 2까지 가는 가중치가 1인 간선을 인접 리스트에 추가한다.
- 3에서 4까지 가는 가중치가 13인 간선을 인접 리스트에 추가한다.
- 4에서 4까지 가는 가중치가 9인 간선을 인접 리스트에 추가한다.
- 4에서 1까지 가는 가중치가 42인 간선을 인접 리스트에 추가한다.
- 왼 쪽에 있는 그래프를 인접 리스트로 표현하면 오른쪽과 같이 된다.
- 그래프의 간선이 7개, 인접 리스트에 있는 간선도 7개다.
- 그래프 구성 요소만 인접 리스트에 추가하기 때문에 메모리 낭비가 없다.
- 리스트를 사용하기 때문에 추가와 삭제가 상대적으로 용이하다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import java.util.ArrayList; import java.util.List; public static void main(String[] args) { // 정점의 수 int V = 4; // 인접 리스트 생성 List<List<int[]>> adjList = new ArrayList<>(); // 인접 리스트 초기화 for (int i = 0; i <= V; i++) { adjList.add(new ArrayList<>()); } // 간선 정보 (시작 정점, 끝 정점, 가중치) int[][] edges = { {1, 2, 3}, {1, 3, 5}, {2, 1, 6}, {2, 3, 5}, {3, 2, 1}, {3, 4, 13}, {4, 4, 9}, {4, 1, 42} }; // 그래프에 간선 추가 for (int[] edge : edges) { int u = edge[0]; // 시작 정점 int v = edge[1]; // 끝 정점 int weight = edge[2]; // 가중치 adjList.get(u).add(new int[]{v, weight}); } // 인접 리스트 출력 for (int i = 1; i <= V; ++i) { System.out.print("Vertex " + i + ":"); for (int[] edge : adjList.get(i)) { System.out.print(" (" + edge[0] + ", " + edge[1] + ")"); } System.out.println(); } }
인접 행렬 VS 인접 리스트
그래프의 탐색
- 그래프를 정해진 순서로 순회하는 방법
- 깊이 우선 탐색(DFS)
- 더 이상 탐색할 노드가 없을 때 까지 방문한다.
- 더 이상 탐색할 노드가 없다면 최근에 방문했던 노드로 돌아가고, 가지 않은 노드 방문
- 너비 우선 탐색(BFS)
- 현재 위치에서 가장 가까운 노드부터 방문하고 다음 노드로 넘어간다.
- 모든 노드를 방문할 때 까지 위 과정 반복
그래프의 탐색 - 깊이 우선 탐색(DFS)
- A -> B -> D 순차적으로 방문
- D 다음 탐색할 노드가 없다.
- D를 방문하기 전에 가장 최근에 방문한 B로 돌아간다. A -> B -> D -> B
- B에서 아직 방문하지 않은 노드가 있으니 깊이 들어간다.
- 노드 E를 방문한다. A -> B -> D -> B -> E
- E 다음 탐색할 노드가 없다.
- E를 방문하기 전에 가장 최근에 방문한 B로 돌아간다. A -> B -> D -> B -> E -> B
- B 다음 탐색할 노드가 없다.
- B를 방문하기 전에 가장 최근에 방문한 A로 돌아간다. A -> B -> D -> B -> E -> B -> A
- A에서 아직 방문하지 않은 노드가 있으니 깊이 들어간다.
- 노드 C를 방문한다. A -> B -> D -> B -> E -> B -> A -> C
- C 다음 탐색할 노드가 없다.
- 모든 노드를 방문했으니 탐색을 종료한다.
- 최종 방문 순서 A -> B -> D -> E -> C
- 다른 탐색 대비 깊이 우선 탐색이 가지는 가장 중요한 특징이 백트래킹이다.
- 위 사진 내용에서 최근 방문한 곳으로 돌아가는 것을 백트래킹이라 한다.
- 깊이 우선 탐색 구현하기
- 계속해서 깊이 탐색할 수 있어야 한다.
- 더 이상 깊은 곳이 없는 경우, 가장 최근에 방문했던 노드로 돌아갈 수 있어야 한다.
- 이미 방문한 노드는 중복해서 방문하지 않아야 한다.
- 가장 최근에 방문했던 노드로 가는 효율적인 방법
- LIFO로 동작하는 스택을 활용한다.
- 방문할 노드 푸시, 방문한 노드는 팝, 스택의 top은 가장 최근에 방문한 노드
- stack이 함수 호출 흐름과 동일하기 때문에 재귀 함수로 구현하는 경우가 많다.
- visited 배열을 활용해서 방문 여부를 확인 후에 노드를 탐색한 후 방문한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import java.util.ArrayList; import java.util.List; public class DFSExample { // 인접 리스트 및 방문 리스트 private static List<Integer>[] adj; private static boolean[] visited; public static void main(String[] args) { int V = 5; // 정점의 개수 // 인접 리스트 초기화 adj = new ArrayList[V]; for (int i = 0; i < V; i++) { adj[i] = new ArrayList<>(); } // 그래프의 간선 정보 (각 배열의 첫 번째 값이 시작 정점, 두 번째 값이 도착 정점) int[][] edges = { {0, 1}, // A -> B {0, 2}, // A -> C {1, 3}, // B -> D {1, 4} // B -> E }; // 인접 리스트에 간선 추가 for (int[] edge : edges) { addEdge(edge[0], edge[1]); } // 방문 배열 초기화 visited = new boolean[V]; System.out.println("깊이 우선 탐색 (정점 0에서 시작):"); // DFS 수행 dfs(0); /* 재귀 호출 과정 설명 (예: 정점 0에서 시작): dfs(0) 호출 -> 0 출력 dfs(1) 호출 -> 1 출력 dfs(3) 호출 -> 3 출력 dfs(4) 호출 -> 4 출력 dfs(2) 호출 -> 2 출력 - dfs_traversal 종료 */ } private static void addEdge(int v, int w) { adj[v].add(w); // 정점 v의 인접 리스트에 정점 w를 추가 } private static void dfs(int v) { // 현재 정점을 방문했다고 표시하고 출력 visited[v] = true; System.out.print(v + " "); // 현재 정점에 인접한 모든 정점을 순회 for (int w : adj[v]) { if (!visited[w]) { dfs(w); } } } }
그래프의 탐색 - 너비 우선 탐색(BFS)
- A 방문
- A에서 가장 가까운 노드 B, C를 방문 (간선 1개를 거친다) A -> B -> C
- 2개의 간선을 거치는 D, E를 방문한다. A -> B -> C -> D -> E
- 모든 노드를 방문했으니 탐색을 끝낸다.
- 너비 우선 탐색 구현하기
- 루트 노드부터 시작해서, 가장 가까운 노드들부터 방문할 수 있어야 한다.
- 루트 -> 1개 간선으로 갈 수 있는 노드 -> 2개 간선으로 갈 수 있는 노드…(모든 노드 방문할 때 까지 반복)
- 가장 가까운 노드부터 방문하는 효율적인 방법
- FIFO로 동작하는 큐를 활용한다.
- 시작 노드 푸시
- 팝 후에 방문 처리 이후, 현재 노드에서 연결된 노드 중 방문하지 않은 노드 모두 푸시
- 모든 노드를 방문할 때 까지 반복한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; public class BFSExample { private static List<Integer>[] adj; // 인접 리스트를 배열로 정의 private static boolean[] visited; // 방문 여부를 기록하는 배열 // 그래프에 간선을 추가하는 메서드 private static void addEdge(int u, int v) { adj[u].add(v); // 정점 u의 인접 리스트에 정점 v를 추가 } // 너비 우선 탐색(BFS)을 수행하는 메서드 private static void bfs(int start) { Queue<Integer> q = new LinkedList<>(); visited[start] = true; q.add(start); while (!q.isEmpty()) { int v = q.poll(); System.out.print(v + " "); for (int w : adj[v]) { if (!visited[w]) { visited[w] = true; q.add(w); } } } } public static void main(String[] args) { int V = 5; // 정점의 개수 adj = new ArrayList[V]; for (int i = 0; i < V; i++) { adj[i] = new ArrayList<>(); // 각 정점의 인접 리스트를 초기화 } // 그래프의 간선 정보 (각 배열의 첫 번째 값이 시작 정점, 두 번째 값이 도착 정점) int[][] edges = { {0, 1}, // A -> B {0, 2}, // A -> C {1, 3}, // B -> D {1, 4} // B -> E }; // 인접 리스트에 간선 추가 for (int[] edge : edges) { addEdge(edge[0], edge[1]); } visited = new boolean[V]; // 방문 배열 초기화 System.out.println("너비 우선 탐색 (정점 0에서 시작):"); bfs(0); /* BFS 호출 과정 설명 (예: 정점 0에서 시작): 1. bfs(0) 호출 -> 0 출력, 인접 정점 1과 2를 큐에 추가 2. 큐에서 1을 꺼내어 출력 -> 인접 정점 3과 4를 큐에 추가 3. 큐에서 2를 꺼내어 출력 -> 인접 정점 없음 4. 큐에서 3을 꺼내어 출력 -> 인접 정점 없음 5. 큐에서 4를 꺼내어 출력 -> 인접 정점 없음 6. 큐가 비어 있으므로 탐색 종료 */ } }
깊이 우선 탐색(DFS) VS 너비 우선 탐색(BFS)
- 백트래킹은 깊이 우선 탐색(DFS)에만 존재
- 스도쿠 문제, 1 ~ 5를 사용해서 합이 10이 되는 모든 경우 등
- 너비 우선 탐색으로 찾은 해만 최적의 해를 보장한다.
- 시작점에서 끝지점까지 가는데 최소한의 움직임
- 너비 우선 탐색은 모든 다음 노드를 큐에 푸쉬하므로 깊이 우선 탐색보다 메모리 사용량이 높다.
- 둘 다 되는 경우가 존재, 애매하면 깊이 우선 탐색 먼저 시도한다.
최단 경로 알고리즘
- 두 노드를 잇는 가중치의 합을 최소로 하는 경로를 찾는 알고리즘
최단 경로를 구하기 위한 아이디어1
최단 경로를 구하기 위한 아이디어2
다익스트라 알고리즘으로 실제 최단 경로 구하기
- 다익스트라 알고리즘
- 가중치가 있는 그래프에서 특정 시작 노드로부터 다른 모든 노드까지의 최단 경로를 찾는 알고리즘
- 그래프의 모든 간선의 가중치가 양수일 때 유효
- 음수 가중치를 가진 간선이 있는 그래프에서는 동작하지 않는다.
- 음수 가중치가 있는 경우 벨만-포드 알고리즘을 사용해야 한다.
- 시작 노드 A, A를 후보 노드로 설정하고 visited에 추가한다. visited[A]
- A에서 갈 수 있는 노드 A -> B(4), A -> C(4), A -> E(1) 최소 비용, 직전 노드 갱신
- 후보 노드 A를 제외 하고, 최소 비용이 제일 적은 E가 다음 후보 노드가 된다.
- E를 후보 노드로 설정하고 visited에 E를 추가한다. visited[A, E]
- E에서 갈 수 있는 노드들 중, 최단 경로를 갱신할 수 있는 노드를 확인한다.
- E -> C(2) 이고, (E까지 가는 최소 비용 1) + (E -> C까지 가는 최소 비용 2)로 C 노드에 저장된 최소 비용과 비교한다.
- C의 현재 최소 비용은 4이다. 3 < 4 이기 때문에 C의 최소 비용을 3으로 갱신한다.
- C가 갱신될 때 직전 노드는 E이다. 직전 노드도 E로 갱신한다.
- 후보 노드 A, E를 제외하고, 최소 비용이 제일 적은 C가 다음 후보 노드가 된다.
- C를 후보 노드로 설정하고 visited에 C를 추가한다. visited[A, E, C]
- C에서 갈 수 있는 노드들 중, 최단 경로를 갱신할 수 있는 노드를 확인한다.
- C -> D(8) 이고, (C까지 가는 최소 비용 3) + (C -> D까지 가는 최소 비용 8)로 D 노드에 저장된 최소 비용과 비교한다.
- D의 현재 최소 비용은 무한대이다. 11 < 무한대이기 때문에 D의 최소 비용을 11로 갱신한다.
- D가 갱신될 때 직전 노드는 C이다. 직전 노드도 C로 갱신한다.
- 후보 노드 A, E, C를 제외하고, 최소 비용이 제일 적은 B가 다음 후보 노드가 된다.
- B를 후보 노드로 설정하고 visited에 B를 추가한다. visited[A, E, C, B]
- B에서 갈 수 있는 노드들 중, 최단 경로를 갱신할 수 있는 노드를 확인한다.
- B -> C(6) 이고, (B까지 가는 최소 비용 4) + (B -> C까지 가는 최소 비용 6)로 C 노드에 저장된 최소 비용과 비교한다.
- C의 현재 최소 비용은 3이다. 10 > 3 이기 때문에 C의 최소 비용은 갱신하지 않는다.
- 최소 비용이 갱신되지 않았으니 C의 직전 노드도 갱신하지 않는다.
- 후보 노드 A, E, C, B를 제외하고 남은 D가 후보 노드가 된다.
- D를 후보 노드로 설정하고 visited에 D를 추가한다. visited[A, E, C, B, D]
- D에서 갈 수 있는 노드들 중, 최단 경로를 갱신할 수 있는 노드를 확인한다.
- D -> A(5) 이고, (D까지 가는 최소 비용 11) + (D -> A까지 가는 최소 비용 5)로 A 노드에 저장된 최소 비용과 비교한다.
- A의 현재 최소 비용은 0이다. 16 > 0 이기 때문에 A의 최소 비용은 갱신하지 않는다.
- 최소 비용이 갱신되지 않았으니 A의 직전 노드도 갱신하지 않는다.
- D -> B(2) 이고, (D까지 가는 최소 비용 11) + (D -> B까지 가는 최소 비용 2)로 B 노드에 저장된 최소 비용과 비교한다.
- B의 현재 최소 비용은 4이다. 13 > 4 이기 때문에 B의 최소 비용은 갱신하지 않는다.
- 최소 비용이 갱신되지 않았으니 B의 직전 노드도 갱신하지 않는다.
- visited에 모든 노드가 담겼으니 더 이상 확인할 노드가 없다. 종료한다.
- 다익스트라 알고리즘 결과 분석
- C까지 가는 경로를 확인한다.
- 최소 비용은 3이다.
- C의 직전 노드는 E다. E로 이동한다.
- E의 직전 노드는 A다. A로 이동한다.
- A의 직전 노드는 A다. 종료한다.
- A -> E -> C가 최소 비용 3으로 C로 가는 최단 경로임을 알 수 있다.
- 다익스트라 알고리즘 구현하기
- 후보 노드는 현재까지 후보 노드로 설정 되지 않았던 노드 중, 최소 비용이 가장 적은 노드
- 최소 비용이 가장 적은 노드는 우선 순위 큐를 활용해서 확인한다.
- 최소 비용이 갱신된 노드는 우선 순위 큐에 푸쉬
- 우선 순위 큐에서 팝을 할 때 visited를 활용해 방문여부를 체크한다.
- 방문했다면 아무 동작도 하지 않는다.
- 방문하지 않았다면 최소 비용을 갱신한다.
- 각 노드까지 최소 비용과 최단 경로가 업데이트 되어야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 class Dijkstra { private static final int INF = Integer.MAX_VALUE; // 전역 변수 선언 private static List<List<Pair>> graph = new ArrayList<>(); // 그래프를 인접 리스트 형태로 저장 private static int[] dist; // 각 노드까지의 최단 거리를 저장 private static int[] prev; // 각 노드의 직전 노드를 저장 private static boolean[] visited; // 방문 여부를 저장 // // Pair 클래스를 정의하여 노드와 거리를 저장 private static class Pair { int node; int distance; Pair(int node, int distance) { this.node = node; this.distance = distance; } } // public static void dijkstra(int start) { int n = graph.size(); // 그래프의 노드 개수 dist = new int[n]; // 최단 거리 배열 prev = new int[n]; // 직전 노드 배열 visited = new boolean[n]; // 방문 여부 배열 // Arrays.fill(dist, INF); // 최단 거리 배열을 무한대로 초기화 Arrays.fill(prev, -1); // 직전 노드 배열을 -1로 초기화 dist[start] = 0; // 시작 노드의 거리는 0으로 설정 // // 최소 힙 (우선순위 큐)을 사용하여 최단 거리를 찾음 PriorityQueue<Pair> pq = new PriorityQueue<>((o1, o2) -> Integer.compare(o1.distance, o2.distance)); pq.offer(new Pair(start, 0)); // 시작 노드를 큐에 추가 // // 큐가 빌 때까지 반복 while (!pq.isEmpty()) { Pair pair = pq.poll(); int u = pair.node; // // 현재 노드까지의 거리가 이미 더 짧은 경로가 있으면 무시 if (visited[u]) continue; visited[u] = true; // // 현재 노드의 모든 인접 노드를 탐색 for (Pair edge : graph.get(u)) { int v = edge.node; // 인접 노드 int weight = edge.distance; // 가중치 // // 더 짧은 경로를 발견한 경우 if (dist[u] + weight < dist[v]) { dist[v] = dist[u] + weight; // 최단 거리 업데이트 prev[v] = u; // 직전 노드 업데이트 pq.offer(new Pair(v, dist[v])); // 큐에 추가 } } } } // // 경로를 출력하는 함수 (재귀 호출 사용) public static void printPath(int node) { if (node == -1) return; // base case: 시작 노드에 도달 printPath(prev[node]); // 직전 노드로 재귀 호출 System.out.print(node + " "); // 현재 노드 출력 } // public static void main(String[] args) { // 그래프를 인접 리스트 형태로 정의 // 각 Pair는 (인접 노드, 가중치)를 의미 graph.add(Arrays.asList(new Pair(1, 4), new Pair(2, 4), new Pair(4, 1))); // A (0) graph.add(new ArrayList<>()); // B (1) graph.add(Arrays.asList(new Pair(1, 6), new Pair(3, 8))); // C (2) graph.add(Arrays.asList(new Pair(1, 2))); // D (3) graph.add(Arrays.asList(new Pair(2, 2))); // E (4) // int start = 0; // 시작 노드 (A) dijkstra(start); // 다익스트라 알고리즘 실행 // // 결과 출력 System.out.println("Node\tDistance\tPath"); for (int i = 0; i < dist.length; ++i) { System.out.print(i + "\t" + dist[i] + "\t\t"); printPath(i); System.out.println(); } } }
음의 가중치가 있는 그래프에서 다익스트라 알고리즘
전력망을 둘로 나누기 문제 풀이
- 전력망을 둘로 나누기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import java.util.*; // class Solution { private static boolean[] visited; private static List<Integer>[] adj; private static int N, answer; // public int solution(int n, int[][] wires) { N = n; answer = n - 1; // adj = new ArrayList[n + 1]; for (int i = 1; i <= n; i++) { adj[i] = new ArrayList<>(); } // for (int[] wire : wires) { adj[wire[0]].add(wire[1]); adj[wire[1]].add(wire[0]); } // visited = new boolean[n + 1]; // dfs(1); // return answer; } // private static int dfs(int now) { visited[now] = true; // int sum = 0; // for (int next : adj[now]) { if (!visited[next]) { int cnt = dfs(next); // answer = Math.min(answer, Math.abs(N - cnt * 2)); // sum += cnt; } } // return sum + 1; } }- 못 풀었다. ㅠㅠ ↑ 풀이 ↑
- dfs의 answer와 sum을 구하는 방법이 이해가 되질 않는다. 계속 풀어보고 복습해야겠다.
양과 늑대 문제 풀이
- 양과 늑대
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Solution { int answer = 0; boolean[] checked; // public int solution(int[] info, int[][] edges) { checked = new boolean[(int) Math.pow(2, info.length)]; dfs(0, new boolean[info.length], 0, 0, info, edges, 1); return answer; } // private void dfs(int idx, boolean[] visited, int sheep, int wolf, int[] info, int[][] edges, int state) { visited[idx] = true; checked[state] = true; // if(info[idx] == 0) { sheep++; answer = Math.max(sheep, answer); } else { wolf++; } // if(sheep <= wolf) { return; } // for (int[] edge : edges) { if(visited[edge[0]] && !visited[edge[1]] && !checked[state | (1<<edge[1])]) { boolean[] newVisited = visited.clone(); dfs(edge[1], newVisited, sheep, wolf, info, edges, state | (1<<edge[1])); } } } }- 못 풀었다. ㅠㅠ ↑ 풀이 ↑
- 1시간을 보고 문제에 손도 못대고 숨이 턱 막혔고, 다른 사람의 풀이를 보고 벽을 느꼈다.
- 계속해서 풀어봐야겠다… 쉽지 않다..
미로 탈출 문제 풀이
- 미로 탈출
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 import java.util.*; // class Solution { // private static final int[] dx = {0, 0, -1, 1}; private static final int[] dy = {-1, 1, 0, 0}; // private static class Point { int nx, ny; // public Point(int nx, int ny) { this.nx = nx; this.ny = ny; } } // private static char[][] map; private static int N, M; // public int solution(String[] maps) { N = maps.length; M = maps[0].length(); // map = new char[N][M]; for (int i = 0; i < N; i++) { map[i] = maps[i].toCharArray(); } // Point start = null, end = null, lever = null; // for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { if (map[i][j] == 'S') start = new Point(j, i); else if (map[i][j] == 'E') end = new Point(j, i); else if (map[i][j] == 'L') lever = new Point(j, i); } } // int startLever = bfs(start, lever); int leverEnd = bfs(lever, end); // if (startLever == -1 || leverEnd == -1) { return -1; } else { return startLever + leverEnd; } } // private static int bfs(Point start, Point end) { int[][] dist = new int[N][M]; ArrayDeque<Point> queue = new ArrayDeque<>(); dist[start.ny][start.nx] = 1; queue.add(start); // while (!queue.isEmpty()) { Point now = queue.poll(); for (int i = 0; i < 4; i++) { int nextX = now.nx + dx[i]; int nextY = now.ny + dy[i]; // if (nextX < 0 || nextX >= M || nextY < 0 || nextY >= N) { continue; } // if (dist[nextY][nextX] > 0) { continue; } // if (map[nextY][nextX] == 'X') { continue; } // dist[nextY][nextX] = dist[now.ny][now.nx] + 1; // queue.add(new Point(nextX, nextY)); // if (nextX == end.nx && nextY == end.ny) { return dist[end.ny][end.nx] - 1; } } } return -1; } }- 못 풀었다. ㅠㅠ ↑ 풀이 ↑
- 그래프가 너무 어렵다 ㅠㅠ 한 문제도 못 풀었다… 심각한 듯 하다.
- 개념을 보고 익숙해질 때 까지 반복 숙달을 해야겠다 ㅠㅠ