diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a09375 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/* + +*.iml \ No newline at end of file diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json new file mode 100644 index 0000000..03aeff6 --- /dev/null +++ b/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": "구성 없음" +} \ No newline at end of file diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json new file mode 100644 index 0000000..6b61141 --- /dev/null +++ b/.vs/VSWorkspaceState.json @@ -0,0 +1,6 @@ +{ + "ExpandedNodes": [ + "" + ], + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/.vs/algorithm-study/v15/.suo b/.vs/algorithm-study/v15/.suo new file mode 100644 index 0000000..3edfa50 Binary files /dev/null and b/.vs/algorithm-study/v15/.suo differ diff --git a/.vs/algorithm-study/v15/Browse.VC.db b/.vs/algorithm-study/v15/Browse.VC.db new file mode 100644 index 0000000..3d10441 Binary files /dev/null and b/.vs/algorithm-study/v15/Browse.VC.db differ diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000..4c1c2c1 Binary files /dev/null and b/.vs/slnx.sqlite differ diff --git "a/contents/\352\270\260\353\263\270\354\240\225\353\240\254.md" "b/contents/\352\270\260\353\263\270\354\240\225\353\240\254.md" index a8539d2..1c0cd7f 100644 --- "a/contents/\352\270\260\353\263\270\354\240\225\353\240\254.md" +++ "b/contents/\352\270\260\353\263\270\354\240\225\353\240\254.md" @@ -5,7 +5,7 @@ - 크기가 n인 일차원 배열에서 첫번쨰 인덱스를 i로 뽑는다. - i의 이후(i+1) 요소중 가장 작은 수(minIndex)를 선택해 i와 자리를 바꾼다. (swap) - i 다음의 인덱스에서 위 순서를 반복한다. -- 복잡도 O(n^2) +- 복잡도 항상 O(n^2) ## 2. 버블정렬 가장 가까운 인덱스끼리 비교하여 swap하는 과정이 거품이 이는 현상같아 '버블'이라는 이름이 붙었다. @@ -13,12 +13,12 @@ - 인덱스 0부터 n-1까지 순회하며 i와 i+1 요소를 비교한다. - 배열을 한번 돌고나면 마지막 요소에는 가장 큰 수가 있다. - 마지막 인덱스를 하나씩 줄여가며 위 순서를 반복한다. -- 복잡도 O(n^2) +- 복잡도 항상 O(n^2) ## 3. 삽입정렬 key를 주체로 삽입하는 알고리즘이다. 어떤 값 뒤에 key를 삽입할 것인가? 가 중요하다. - 크기가 n인 일차원 배열 - key는 인덱스 1번부터 시작한다. - 배열을 인덱스 0부터 방문하며 key보다 크면 우측으로 모두 밀고 작으면 그 요소의 바로 뒤에 삽입한다. -- 어느정도 정렬이 되어있는 경우에는 O(n^2)지만 최악의 평균적으로 복잡도 O(n^2)를 갖는다. +- 어느정도 정렬이 되어있는 경우에는 O(n)지만 최악의 평균적으로 복잡도 O(n^2)를 갖는다. - 밀어야 하는 요소가 많은 경우 i/o가 많이 일어나는 단점이 있다. \ No newline at end of file diff --git "a/docs/\354\204\234\353\241\234 \353\213\244\353\245\270 \354\226\270\354\226\264\353\241\234 \354\225\214\352\263\240\353\246\254\354\246\230 \355\225\250\352\273\230 \352\263\265\353\266\200\355\225\230\352\270\260.md" "b/docs/\354\204\234\353\241\234 \353\213\244\353\245\270 \354\226\270\354\226\264\353\241\234 \354\225\214\352\263\240\353\246\254\354\246\230 \355\225\250\352\273\230 \352\263\265\353\266\200\355\225\230\352\270\260.md" new file mode 100644 index 0000000..446986d --- /dev/null +++ "b/docs/\354\204\234\353\241\234 \353\213\244\353\245\270 \354\226\270\354\226\264\353\241\234 \354\225\214\352\263\240\353\246\254\354\246\230 \355\225\250\352\273\230 \352\263\265\353\266\200\355\225\230\352\270\260.md" @@ -0,0 +1,34 @@ + + +### 서로 다른 언어로 알고리즘 함께 공부하기 + +- 기존에 사용하던 언어 => Java + +- 새로 추가된 언어 => JavaScript + +- 제안하는 방법 + - 코드리뷰 시작 전 알고리즘 이론 정리/분석 설명하기 + - 팀원들과 이 알고리즘에 대해 얘기하는 시간 갖기 + - JavaScript로 문제를 푸는 것은 옵션! + + + +#### 어떤 알고리즘을 선택할 것인가? + +기존 알고리즘 문제는 그날 문제 리뷰가 끝나고 다음 주에 어떤 문제를 풀지 얘기를 나누면서 선택한다. + +- 오늘의 알고리즘! (요즘 각광받고 있는 알고리즘이나 마이너한 알고리즘) + +- 기본적인 알고리즘 (BFS,DFS,DP...) + +- 팀원들이 푸는 문제와 관련있는 알고리즘 + + + +#### 무엇을 설명할 것인가? + +- 동작 방식 +- 복잡도 +- 장/단점 +- 비슷한 알고리즘 유형 +- 대표적인 문제 소개 (풀이는 개인적으로 작성해 업로드) diff --git a/source/.DS_Store b/source/.DS_Store new file mode 100644 index 0000000..4fd3577 Binary files /dev/null and b/source/.DS_Store differ diff --git a/source/diane/[baekjoon]1005.md b/source/diane/[baekjoon]1005.md new file mode 100644 index 0000000..17db1a3 --- /dev/null +++ b/source/diane/[baekjoon]1005.md @@ -0,0 +1,135 @@ +# Baekjoon ACM Craft 문제 + +- ACM Craft + - https://www.acmicpc.net/problem/1005 + +### 문제 이해하기 +ACM Craft는 건물을 짓는 게임으로, 특정 건물을 지으면 승리할 수 있다. +건물은 짓는데 걸리는 건설 시간이 있고, 어떤 건물은 다른 특정 건물이 지어진 후에만 건설할 수 있다. +승리를 위한 건물을 가장 빨리 지을 때까지 걸리는 최소 시간을 알아내자. + +#### 입력 +- int T : 테스트케이스 개수 + - 이 아래 입력은 테스트케이스 개수만큼 반복됨 +- int N, K : 건물의 개수 N, 건설 순서 규칙의 개수 K +- int D1, D2, ... , DN : 각 건물의 건설에 걸리는 시간 +- int X, Y (*K) : 건설 순서 규칙으로, X 건물이 지어진 후에 Y 건물을 지을 수 있다는 뜻 + - 건설 순서 규칙이 K번 반복하여 주어짐 +- int W : 승리하기 위해 지어야할 목표 건물 + +#### 출력 +- int : 목표 건물을 건설 완료하기까지 드는 최소 시간 + +### 문제 접근 방법 +현재 지을 수 있는 건물들을 찾아 지금 지을 수 있는 건물 목록에 추가한다. +건물은 이 목록에 추가된 순간부터 즉시 건설되기 시작한다고 가정한다. +이 목록에서 건설 시간이 가장 짧은 건물을 찾아, 이 건물을 목록에서 제거하고 그 건물이 다 지어진 후의 시간으로 이동한다. +지금 지을 수 있는 건물 목록의 건물들은 그 건설 시간만큼 이미 지어졌으므로, 이 건물들을 건설하는데 걸리는 시간에서 그만큼 빼준다. +이 시점에서 지금 지을 수 있는 건물 목록을 업데이트하고, 위 과정을 반복한다. +그러다가 목표 건물이 지금 지을 수 있는 건물 목록에 추가되는 순간, 그 건물을 건설 시간에 관계 없이 즉시 건설하고 총 시간을 반환한다. + +### 구현 배경 지식 +- Topological Sorting + +### 문제를 해결한 코드 +~~~java +import java.util.ArrayList; +import java.util.Scanner; + +public class Main { + + // i번 건물이 지어진 후 지을 수 있는 건물 번호들 + static ArrayList> nextBuilding; + // i번 건물이 지어지기 전에 먼저 지어져야 할 건물들 수 + static int[] numOfSource; + // i번 건물이 지어지는 데에 걸리는 시간 + static int[] runningTime; + + static int buildings; + static int rules; + static int target; + + public static void main(String args[]) { + Scanner sc = new Scanner(System.in); + int testCase = sc.nextInt(); + + for (int t=0; t startBuilding = new ArrayList<>(); + while (true) { + // 앞에 해야하는 것이 없는 것들 모으기 + // = 지금 지을 수 있는 건물 + for (int i=0; i runningTime[building]) { + minTime = runningTime[building]; + minBuilding = building; + } + } + time += minTime; + startBuilding.remove((Integer) minBuilding); + for (int b : nextBuilding.get(minBuilding)) { + numOfSource[b]--; + } + for (int b : startBuilding) { + runningTime[b]-=minTime; + } + } + + return time; + } + + private static void getInput(Scanner sc) { + buildings = sc.nextInt(); + rules = sc.nextInt(); + + nextBuilding = new ArrayList<>(); + numOfSource = new int[buildings]; + runningTime = new int[buildings]; + for (int i=0; i()); + numOfSource[i] = 0; + runningTime[i] = sc.nextInt(); + } + + for (int i=0; i visited; + private static double[] Direction; + private static int[] Path; + + public static void main(String args[]) throws Exception { + Scanner sc = new Scanner(System.in); + Number = sc.nextInt(); + Path = new int[Number]; + Direction = new double[4]; + for (int i=0; i<4; i++) { + int temp = sc.nextInt(); + Direction[i] = (double) temp/100; + } + + position = new Position(); + visited = new HashSet<>(); + visited.add(position); + double simple = backtracking(0); + + System.out.println(simple); + } + + private static double backtracking(int depth) throws Exception { + if (depth == Number) { + return calculate(); + } + + double result = 0; + for (int i=0; i<4; i++) { + position.move(i); + Path[depth] = i; + if (isSimple()) { + visited.add(position); + result += backtracking(depth+1); + visited.remove(position); + } + position.back(i); + } + return result; + } + + private static boolean isSimple() { + return !visited.contains(position); + } + + private static double calculate() { + double result = 1; + for (int d : Path) { + result = result*Direction[d]; + } + return result; + } +} + +class Position extends Object { + public int x; + public int y; + + Position() { + x=0; y=0; + } + + Position(int x, int y) { + this.x = x; this.y = y; + } + + public void move(int direction) throws Exception { + if (direction == 0) { + x++; + } else if (direction == 1) { + x--; + } else if (direction == 2) { + y--; + } else if (direction == 3) { + y++; + } else { + throw new Exception("Invalid direction " + direction); + } + } + + public void back(int direction) throws Exception { + if (direction == 0) { + x--; + } else if (direction == 1) { + x++; + } else if (direction == 2) { + y++; + } else if (direction == 3) { + y--; + } else { + throw new Exception("Invalid direction " + direction); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass()!=o.getClass()) return false; + Position p = (Position) o; + return (x==p.x)&&(y==p.y); + } + + @Override + public int hashCode() { + return x*100+y; + } +} +~~~ \ No newline at end of file diff --git a/source/diane/[baekjoon]14888.md b/source/diane/[baekjoon]14888.md new file mode 100644 index 0000000..29e746c --- /dev/null +++ b/source/diane/[baekjoon]14888.md @@ -0,0 +1,101 @@ +# Baekjoon 연산자 끼워넣기 + +- 연산자 끼워넣기 + - hhttps://www.acmicpc.net/problem/14888 + +### 문제 이해하기 +N개의 수로 이루어진 수열이 주어지고, 이 N개의 수 사이에 끼워넣을 (N-1)개의 연산자가 주어진다. +연산자는 덧셈(+), 뺄셈(-), 곱셈(x), 나눗셈(/)으로만 이루어져 있다. +수열의 순서를 바꾸지 않으면서, 수 사이에 이 연산자를 끼워넣어 여러 개의 수식을 만들 수 있다. +이렇게 만들 수 있는 수식의 계산 결과가 최대인 것과 최소인 것을 구하여라. +(단, 수식의 결과값이나 중간 과정값은 항상 -10억보다 크거나 같고, 10억보다 작거나 같다.) + +#### 입력 +- int N : 수열의 길이 (수의 개수, 2<=N<=11) +- int A1, A2, ... , AN : 수열의 숫자 (1<=A<=100) +- int op1, op2, op3, op4 : 사용할 수 있는 덧셈, 뺄셈, 곱셈, 나눗셈의 개수 + +#### 출력 +- int : 최대인 수식의 결과 값 +- int : 최소인 수식의 결과 값 + +### 문제 접근 방법 +모든 경우를 dfs로 탐색하면서, 만들어진 수식의 값이 지금까지의 결과 중 최대이거나 최소인 경우 그 값을 저장한다. + + +### 구현 배경 지식 +- Backtracking +- Depth-First Searching + +### 문제를 해결한 코드 +~~~java +import java.util.Scanner; + +public class Main { + + private static int[] numbers; + private static int[] operations; + private static int MIN; + private static int MAX; + + public static void main(String args[]) { + Scanner sc = new Scanner(System.in); + int size = sc.nextInt(); + + numbers = new int[size]; + for (int i=0; i MAX) { + MAX = number; + } + if (number < MIN) { + MIN = number; + } + return; + } + + for (int op = 0; op<4; op++) { + if (operations[op] > 0) { + operations[op]--; + int newNumber = calculate(op, numbers[idx], number); + putOperation(idx+1, newNumber); + operations[op]++; + } + } + } + + private static int calculate(int op, int num, int number) throws Exception { + if (op == 0) { + return number+num; + } else if (op == 1) { + return number-num; + } else if (op == 2) { + return number*num; + } else if (op == 3) { + return number/num; + } + throw new Exception("Invaild operation."); + } +} +~~~ \ No newline at end of file diff --git a/source/diane/[baekjoon]15684.md b/source/diane/[baekjoon]15684.md new file mode 100644 index 0000000..05bdc57 --- /dev/null +++ b/source/diane/[baekjoon]15684.md @@ -0,0 +1,132 @@ +# Baekjoon 사다리 조작 문제 + +- 사다리 조작 + - https://www.acmicpc.net/problem/15684 + +### 문제 이해하기 +사다리 게임이 있다. 이 사다리 게임은 N개의 세로선과 그 세로선들 사이를 잇는 M개의 가로선으로 이루어져 있다. +이 사다리 게임에서는 세로선에 총 H개의 가로선을 놓을 수 있는데, 먼저 그려진 가로선도 그 위치에 맞추어 그려져 있다. +사다리 게임에서 가로선은 인접한 두 세로선을 연결하고, 두 가로선이 연속하거나 접하는 일은 없다. +주어진 사다리 게임에 가로선을 추가하여 i번 세로선의 결과가 i가 나오도록 만들고 싶다. +최대 3개의 가로선을 추가하여 조건에 맞는 사다리 게임을 만들 때, 최소 몇 개의 가로선을 추가해야 하는지 구하여라. + +#### 입력 +- int N M H : 사다리 게임의 세로선, 가로선 갯수와 세로선에 가로선을 놓을 수 있는 위치의 갯수 +- int a b (x M) : 사다리 게임의 M개의 가로선 위치 + - b번째 세로선과 (b+1)번째 세로선을 잇는, 세로로 a번 위치에 가로선이 존재 + +#### 출력 +- int K : 조건을 만족하기 위해 추가해야 하는 가로선 갯수의 최솟값 + - 정답이 3보다 큰 경우, 또는 불가능한 경우에는 -1을 반환 + +### 문제 접근 방법 +주어진 사다리 게임에 비어있는 모든 가로선 자리에 가로선을 최대 3개까지 그리면서 조건에 맞는지를 확인할 것이다. +이에 따라 아래와 같은 상태 공간 트리를 그릴 수 있다. + +(가로선 0개)                                  추가된가로선없음 +                                                    /                       \ +(가로선 1개)                       1번자리가로선            2번자리가로선    ... +                                         /                    \ +(가로선 2개)           2번자리가로선           3번자리가로선   ... +                              /                 \ +(가로선 3개) 3번자리가로선 4번자리가로선   ... + +이 상태 공간 트리를 dfs로 탐색하면서, 각 노드에서 문제의 요구 조건을 충족하는지 확인할 것이다. +각 노드를 탐색할 때 그 노드가 문제의 기준을 만족하지 않는다면, 그 노드의 자식 노드는 탐색하지 않고, 그 노드의 부모 노드로 돌아가 다른 노드를 탐색할 것이다. +이 때 사용되는 기준 함수(bounding function)은 다음과 같다. 해당 노드의 상태에서 추가될 가로선이 (1) 이미 존재하지 않고 (2) 왼쪽이나 오른쪽의 가로선과 만나서는 안 된다. + +가능성 있는 모든 상태를 탐색하면서, 그 중 조건을 만족하는 상태에서 그 depth를 반환한다. +추가할 가로선이 어떤 가로선인지를 알고 싶은 것이 아니라, 몇 개의 가로선이 추가되어야 하는지를 알기 위해 그 깊이를 반환하고, 그 깊이 중 최소값을 반환한다. +만약 깊이가 3을 탐색했는데 조건을 만족하지 않는다면, 그 이상은 탐색하지 않고 -1을 반환한다. + +### 구현 배경 지식 +- backtracking +- dfs + +### 문제를 해결한 코드 +~~~java +import java.util.Scanner; + +public class Main { + private static int N, M, H; + private static boolean[][] ladder; + private static int Limit = 3; + + public static void main(String args[]) { + Scanner sc = new Scanner(System.in); + N = sc.nextInt(); + M = sc.nextInt(); + H = sc.nextInt(); + ladder = new boolean[N][H]; + + for (int i=0; i iter) Limit = iter; + return iter; + } + + if (iter == Limit) { + return -1; + } + + int result = -1; + for (int i=idx; i temp)) result = temp; + if (result == iter+1) return result; + } + } + } + return result; + } + + private static boolean makeLadder(int lad, int step) { + if (ladder[lad][step]) return false; + if (lad>0 && ladder[lad-1][step]) return false; + if (lad tile; + private HashMap sharks; + + Solution(String file) { + tile = new HashMap<>(); + sharks = new HashMap<>(); + if (file == null) { + setData(); + } else { + readFromFile(file); + } + } + + public int run() { + int iter = 0; + while (iter < 1000) { + iter ++; + ArrayList ss = new ArrayList<>(sharks.keySet()); + ArrayList pp = new ArrayList<>(tile.keySet()); + Collections.sort(ss); + for (int s : ss) { + nextPosition(s); + } + for (Position p : pp) { + State state = tile.get(p); + if (state.next()) { + tile.put(p, state); + } else { + tile.remove(p); + } + } + for (int s : ss) { + Position p = sharks.get(s).position; + if (tile.get(p) == null) { + tile.put(new Position(p.x,p.y), new State(s,k)); + } else if (tile.get(p).shark == s) { + tile.put(new Position(p.x,p.y), new State(s,k)); + } else { + sharks.remove(s); + } + } + if (sharks.size() < 2) return iter; + } + + return -1; + } + + private Position nextPosition(int s) { + Shark shark = sharks.get(s); + Position p = shark.position; + ArrayList next = shark.next.get(shark.direction-1); + boolean flag = false; + for (int d : next) { + shark.direction = d; + if (d == 1 && p.y>0) { + if (tile.get(new Position(p.x, p.y-1))!= null) continue; + p.y--; + flag = true; break; + } else if (d==2 && p.y0) { + if (tile.get(new Position(p.x-1, p.y))!= null) continue; + p.x--; + flag = true; break; + } else if (d==4 && p.x 0) { + if (tile.get(new Position(p.x, p.y-1)).shark != s) continue; + p.y--; + flag = true; break; + } else if (d == 2 && p.y < N - 1) { + if (tile.get(new Position(p.x, p.y+1)).shark != s) continue; + p.y++; + flag = true; break; + } else if (d == 3 && p.x > 0) { + if (tile.get(new Position(p.x-1, p.y)).shark != s) continue; + p.x--; + flag = true; break; + } else if (d == 4 && p.x < N - 1) { + if (tile.get(new Position(p.x+1, p.y)).shark != s) continue; + p.x++; + flag = true; break; + } + } + } + if (!flag) System.out.println("ERROR"); + shark.position = p; + sharks.put(s, shark); + return new Position(p.x,p.y); + } + + private void readFromFile(String filename) { + File file = new File(filename); + + try { + FileReader reader = new FileReader(file); + BufferedReader breader = new BufferedReader(reader); + + String line = breader.readLine(); + String[] temp = line.split(" "); + N = Integer.parseInt(temp[0]); + M = Integer.parseInt(temp[1]); + k = Integer.parseInt(temp[2]); + + for (int i=0; i dir = new ArrayList<>(); + for (String t : temp) { + dir.add(Integer.parseInt(t)); + } + s.next.add(dir); + } + sharks.put(i, s); + } + + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void printTiles() { + int[][] tiles = new int[N][N]; + tile.forEach((Position p, State s) -> { + tiles[p.y][p.x] = s.shark*10+s.smell; + }); + sharks.forEach((Integer n, Shark s) -> { + tiles[s.position.y][s.position.x] = s.num*10+s.direction; + }); + System.out.println("-----------------------------------------------"); + for (int i=0; i> next; + + Shark(int i, Position p) { + num = i; + position = p; + next = new ArrayList<>(); + } +} + +public class Position extends Object { + public int x; + public int y; + + Position(int x, int y) { + this.x = x; + this.y = y; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass()!=o.getClass()) return false; + Position p = (Position) o; + return (x==p.x)&&(y==p.y); + } + + @Override + public int hashCode() { + return x*100+y; + } +} + +public class State { + int shark; + int smell; + + State(int shark, int smell) { + this.shark = shark; + this.smell = smell; + } + + public boolean next() { + this.smell--; + if (smell > 0) { + return true; + } + return false; + } + + public String toString() { + return shark+" "+smell; + } +} +``` + + +### 그 외 참고사항 +HashMap에서 Key를 찾을 때에, +HashMap에 있는 key와 주어진 key를 비교할 때, +hashcode()를 불러 먼저 비교하고, equals() 함수를 불러 true이면 같다고 판단한다. diff --git a/source/diane/[baekjoon]1647.md b/source/diane/[baekjoon]1647.md new file mode 100644 index 0000000..2264ded --- /dev/null +++ b/source/diane/[baekjoon]1647.md @@ -0,0 +1,148 @@ +# Baekjoon 도시 분할 계획 문제 + +- 도시 분할 계획 + - https://www.acmicpc.net/problem/1647 + +### 문제 이해하기 +N개의 집과 이 집들 사이를 잇는 M개의 길이 있는 마을이 있다. +이 길은 양쪽 방향 모두로 다닐 수 있고, 길을 유지하는데 드는 유지비가 있다. +이 마을을 두 개의 분리하려고 한다. +각 마을에 있는 집들은 모두 연결되어야 하는데, 유지비가 최소가 되게끔만 길을 남기려고 한다. +두 마을은 서로 다른 마을과는 연결되지 않는다. +이 때 이 마을들의 길 유지비의 최솟값을 구하라. + +#### 입력 +- int N, M : 집의 개수, 길의 개수 + - N : integer, such that 2 <= N <= 100,000 + - M : integer, such that 1 <= M <= 1,000,000 +- int 3개 * M : 길이 연결하는 집의 번호 두 개, 그 길의 유지비 + - 길의 유지비 C : integer, such that 1 <= C <= 1,000 + +#### 출력 +- int : 남은 길의 유지비의 합의 최솟값 + +### 문제 접근 방법 +길의 유지비를 최소로 하도록 마을을 둘로 나누는 것은, +기존 마을을 MST로 만든 다음 가장 유지비가 큰 길을 없애서 나누면 원하는 조건대로 만들 수 있다. + +길의 갯수가 주어지고, Prim 알고리즘으로 풀기에는 각 집을 지나는 길을 찾는데 비용이 들어, +이보다는 Kruskal로 푸는 것이 유리하다고 판단했다. + +먼저 모든 길을 유지비의 오름차순으로 정렬한다. +유지비가 적은 길부터 차례대로, 길의 양쪽 집이 이미 연결되어 있는지 확인하고, 연결되어 있지 않으면 이 길을 선택한다. +선택한 길이 N-1개라는 것은 MST를 만들었다는 것이기 때문에, +이 때는 마지막으로 찾은 길을 포함하지 않고 (마을을 둘로 만들어야 하니까) 지금까지 선택한 길의 유지비의 합을 출력한다. + +길의 양쪽 집이 이미 연결되어 있는지를 확인할 때는, +union-find 알고리즘을 사용하였다. + + +### 구현 배경 지식 +- MST (Kruskal Algorithm) +- Union-Find + +### 문제를 해결한 코드 +~~~java +import java.util.*; + +public class Main { + private static int N; + private static int M; + private static int[] disjointSet; + + public static void main(String args[]) { + Scanner sc = new Scanner(System.in); + N = sc.nextInt(); // vertex + M = sc.nextInt(); // edge + + ArrayList paths = new ArrayList<>(); + for (int i=0; i { + public int house1; + public int house2; + public int cost; + + Path(int h1, int h2, int c) { + house1 = h1; + house2 = h2; + cost = c; + } + + @Override + public int compareTo(Path b) { + return this.cost - b.cost; + } +} +~~~ + +### 새로 알게 된 내용 +ArrayList의 remove 함수가 굉장히 시간적 cost가 큰 로직이라는 것을 새로 알게 되었다. +초기에는 길을 선택할 때, 유지비로 정렬한 다음 리스트의 가장 첫 원소를 가져오고 삭제하고 이를 반복했는데, +이 때는 시간 초과가 발생한다. +이 후 길의 리스트를 돌면서 get으로 길을 가져오도록 변경하자, 알고리즘은 동일하지만, 시간 초과 없이 성공했다. +이는 두 함수의 로직 차이에서 기인한다. +remove 함수는 그 원소를 제거한 후에 그 뒤의 원소를 모두 한 칸 씩 앞으로 새로 저장하기 때문에 눈에 띄게 오래 걸리는 것이다. +```java + public E get(int index) { + rangeCheck(index); + + return elementData(index); + } + + public E remove(int index) { + rangeCheck(index); + + modCount++; + E oldValue = elementData(index); + + int numMoved = size - index - 1; + if (numMoved > 0) + System.arraycopy(elementData, index+1, elementData, index, + numMoved); + elementData[--size] = null; // clear to let GC do its work + + return oldValue; + } +``` \ No newline at end of file diff --git a/source/diane/[baekjoon]1931.md b/source/diane/[baekjoon]1931.md new file mode 100644 index 0000000..0c9cc0d --- /dev/null +++ b/source/diane/[baekjoon]1931.md @@ -0,0 +1,84 @@ +# Baekjoon 회의실 배정 + +- 회의실 배정 + - https://www.acmicpc.net/problem/1931 + + +### 문제 이해하기 +N개의 회의가 있는데, 회의실은 하나이다. 각 회의에 대해 시작시간과 끝나는 시간이 주어지고, +회의가 겹치지 않게 회의실을 사용할 때 할 수 있는 회의의 최대 개수를 찾아라. +회의는 중단되지 않으며, 회의가 끝남과 동시에 다른 회의가 시작될 수 있다. + + +#### 입력 +- int N : 회의의 개수 +- int Start End : N번 반복되며, 각 회의의 시작시간과 끝시간 + + +#### 출력 +- int : 할 수 있는 회의의 최대 개수 + + +### 문제 접근 방법 +회의실에서 어떤 회의가 진행된다면, 그 회의가 빨리 끝날수록 그 회의실에서 더 많은 회의를 할 가능성이 증가한다. +따라서 지금 회의실에서 할 수 있는 회의 중 가장 빨리 끝나는 회의를 해야 한다. + + +### 구현 배경 지식 +- Greedy Algorithm + + +### 문제를 해결한 코드 +~~~java +import java.util.ArrayList; +import java.util.Collections; +import java.util.Scanner; + +public class Main { + public static void main(String args[]) { + Scanner sc = new Scanner(System.in); + ArrayList meetings = new ArrayList<>(); + + int count = sc.nextInt(); + for (int i=0; i= time) { + number++; + time = meeting.end; + } + } + + System.out.println(number); + } +} + +class Meeting implements Comparable { + public int start; + public int end; + + public Meeting(int start, int end) { + this.start = start; + this.end = end; + } + + @Override + public int compareTo(Meeting m) { + if (end == m.end) + return start - m.start; + return end - m.end; + } + + @Override + public String toString() { + return "Meeting{" + start + "~" + end + '}'; + } +} +~~~ \ No newline at end of file diff --git a/source/diane/[baekjoon]2042.md b/source/diane/[baekjoon]2042.md new file mode 100644 index 0000000..5ca7ae0 --- /dev/null +++ b/source/diane/[baekjoon]2042.md @@ -0,0 +1,116 @@ +# Baekjoon 구간 합 구하기 + +- 구간 합 구하기 + - https://www.acmicpc.net/problem/2042 + +### 문제 이해하기 +N개의 숫자가 주어져 있다. 이 N개의 숫자를 가지고, 이 중 특정한 수의 값을 바꾸거나, 이 중 특정 구간의 수들의 합을 구하는 작업을 하려고 한다. +수의 값을 바꾸고, 구간의 합을 구하는 작업이 들어올 때, 이를 작업이 들어올 때마다 실행하며, 합을 구하면 그 값을 반환해라. + +#### 입력 +- int N M K : 주어진 숫자의 갯수, 수의 변경이 일어나는 횟수, 구간의 합을 구하는 횟수 +- long NUM : N번 반복하면서 주어진 숫자를 입력 +- int A B (long) C : (M+K)번 반복 + - A==1 : 수의 변경, B번째 수를 C의 값으로 바꾸어야 한다. + - A==2 : 구간의 합, B번째 수부터 C번째 수까지의 합을 구하고 출력한다. + +#### 출력 +- int : K번 반복하면서, 구간의 합을 출력 + +### 문제 접근 방법 +주어진 숫자 배열로 먼저 segment tree를 만든다. +이후로는 숫자의 값을 바꿀 때는 tree의 root부터 값의 변화량을 적용시키면서, 해당 index의 leaf의 값까지 변화시킨다. +구간의 합을 구할 때는 tree의 root부터 검색하면서, 원하는 구간에 해당하는 node의 값을 더하여 합을 구한다. + + +### 구현 배경 지식 +- Segment Tree + +주어진 숫자의 갯수가 N, 수의 변경이 M번, 구간 합을 K번 구한다고 가정하자. + +| 작업 | 일반 | segment tree | +|-----|:----:|:------------:| +|값 변경하기|O(1)|O(logN)| +|구간 합 구하기|O(N)|O(logN)| +|모든 작업|O(NK)|O((M+K)logN)| + +작업의 수(K)가 많을수록, 주어진 숫자의 갯수(N)이 많을수록, segment tree가 유리하다. + + +### 문제를 해결한 코드 +~~~java +import java.util.Scanner; + +public class Main { + private static long[] array; + private static long[] segTree; + + public static void main(String args[]) throws Exception { + Scanner sc = new Scanner(System.in); + + int N = sc.nextInt(); + int M = sc.nextInt(); + int K = sc.nextInt(); + + array = new long[N]; + for (int i=0; i end) return; + + segTree[node] += diff; + if (start == end) return; + int mid = (start+end)/2; + updateSegTree(index, diff, node*2, start, mid); + updateSegTree(index, diff, node*2+1, mid+1, end); + } + + private static long sumSegTree(int node, int start, int end, int left, int right) { + if (left > end || right < start) return 0; + if (left <= start && end <= right) return segTree[node]; + int mid = (start+end)/2; + return sumSegTree(node*2, start, mid, left, right) + sumSegTree(node*2+1, mid+1, end, left, right); + } +} +~~~ \ No newline at end of file diff --git a/source/diane/[baekjoon]2583.md b/source/diane/[baekjoon]2583.md new file mode 100644 index 0000000..e377d1b --- /dev/null +++ b/source/diane/[baekjoon]2583.md @@ -0,0 +1,121 @@ +# Baekjoon 영역 구하기 문제 + +- 영역 구하기 + - https://www.acmicpc.net/problem/2583 + +### 문제 이해하기 +MxN 크기의 모눈종이가 있다. +이 모눈종이에 눈금을 맞추어 k개의 직사각형을 그리면, +모눈종이가 이 직사각형의 내부를 제외한 나머지 부분으로 나뉘게 된다. +이 때 만들어진 나머지 영역이 몇 개이고, 그 넓이는 각각 얼마인지 구하라. + +#### 입력 +- int M, N, K : 모눈종이의 세로 길이, 가로 길이, 그려진 직사각형의 개수 + - 모두 100 이하의 자연수 +- int 4개 * k : k개의 직사각형의 왼쪽 아래 꼭지점의 좌표와 오른쪽 위 꼭지점의 좌표 + +#### 출력 +- int : 분리된 영역의 개수 +- int 여러 개 : 각 영역의 넓이 (띄어쓰기로 구분) + +### 문제 접근 방법 +모눈종이의 각 칸은 아래 두가지로 구분한다. +- visited = true : 직사각형의 내부로, 문제에서 필요 없는 부분이다. + 또한, 이미 검사한 나머지 부분으로, 이미 검사했으니 다시 검사할 필요가 없는 부분도 이렇게 나타낸다. +- visited = false : 직사각형에 의해 생긴 나머지 부분으로, 앞으로 검사해야 할 부분이다. + +문제를 해결하기 위해 사용한 방법은 아래와 같다. +1. 모눈종이에서 아직 검사하지 않은 나머지 부분을 찾는다. +2. 찾은 나머지 부분이 포함된 나머지 영역을 찾고, 그 넓이를 구한다. + - 검사하지 않은 나머지 부분과 연결된 검사하지 않은 나머지 부분을 찾는다. + - 새로 찾은 검사하지 않은 나머지 부분에 대하여 이를 반복한다. + - 더 이상 연결된 검사하지 않은 나머지 부분이 없다면, 이 부분이 포함된 영역은 모두 찾은 것이기 때문에 넓이를 기록하고 종료한다. +3. 1-2번을 더 이상 검사하지 않은 나머지 부분이 없을 때까지 반복한다. + + +### 구현 배경 지식 +BFS + +### 문제를 해결한 코드 +~~~java +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Scanner; + +public class Main { + + public static void main(String args[]) { + Scanner sc = new Scanner(System.in); + int m = sc.nextInt(); + int n = sc.nextInt(); + int k = sc.nextInt(); + boolean[][] matrix = new boolean[n][m]; + ArrayList width = new ArrayList<>(); + + for (int c=0; c queue = new ArrayList<>(); + for (int i=0; i x).toArray(); + int x = position[0]; + int y = position[1]; + if (matrix[x][y]) continue; + + matrix[x][y] = true; + count++; + + if (x-1>=0 && !matrix[x-1][y]) queue.add(new Integer[] {x-1,y}); + if (x+1=0 && !matrix[x][y-1]) queue.add(new Integer[] {x,y-1}); + if (y+1 cups; + + public static void main(String args[]) { + Scanner sc = new Scanner(System.in); + int sugar = sc.nextInt(); + + cups = new HashMap<>(); + cups.put(3,1); + cups.put(5,1); + cups.put(1,-1); + cups.put(2,-1); + cups.put(4,-1); + cups.put(6,2); + + System.out.println(calculate(sugar)); + } + + private static int calculate(int sugar) { + if (cups.containsKey(sugar)) return cups.get(sugar); + + int cup = -1; + int cup1 = calculate(sugar-5); + int cup2 = calculate(sugar-3); + + if (cup1 != -1) { + if (cup2 != -1) { + cup = (cup1 > cup2? cup2:cup1)+1; + } else { + cup = cup1+1; + } + } else if (cup2 != -1) { + cup = cup2+1; + } + + cups.put(sugar, cup); + + return cup; + } +} +~~~ \ No newline at end of file diff --git a/source/diane/[baekjoon]4256.md b/source/diane/[baekjoon]4256.md new file mode 100644 index 0000000..503437e --- /dev/null +++ b/source/diane/[baekjoon]4256.md @@ -0,0 +1,135 @@ +# Baekjoon 트리 문제 + +- 트리 + - https://www.acmicpc.net/problem/4256 + +### 문제 이해하기 +어떤 이진 트리가 주어진다. 이 이진 트리는 노드 n개로 이루어져있고, 각 노드는 1부터 n까지의 유일한 번호를 가지고 있다. +이 이진 트리를 전위 순회(preorder)한 결과와 중위 순회(inorder)한 결과가 주어졌을 때, +후위 순회(postorder)한 결과를 구하여라. + +#### 입력 +- int T : 테스트케이스 개수 + - 이 아래 입력은 테스트케이스 개수만큼 반복됨 +- int n : 이진 트리의 노드 갯수 +- int o1, o2, ... , on : 이진 트리를 전위 순회한 결과 +- int o1, o2, ... , on : 이진 트리를 중위 순회한 결과 + +#### 출력 +- int o1, o2, ... , on : 이진 트리를 후위 순회한 결과 (테스트케이스 개수 만큼) + +### 문제 접근 방법 +주어진 전위 순회 결과와 중위 순회 결과를 가지고 이진 트리를 복원한 다음 이 이진 트리를 후위 순회한 결과를 출력한다. + +먼저, 전위 순회 결과와 중위 순회 결과로 이진 트리를 복원해야 한다. +전위 순회는 root 노드를 먼저 출력하고, 그 노드의 왼쪽 자식 노드로 가 전위 순회하고, 이후에 오른쪽 자식 노드로 가 전위 순회한다. +따라서 전위 순회 결과는 언제나 (root 노드) (그 노드의 왼쪽 자식 노드가 root인 트리를 전위 순회한 결과) (그 노드의 오른쪽 자식 노드가 root인 트리를 전위 순회한 결과)이다. +중위 순회는 왼쪽 자식 노드를 중위 순회한 결과를 출력하고, 그 노드를 출력한 다음, 오른쪽 자식 노드를 중위 순회한 결과를 출력한다. +따라서 중위 순회 결과는 언제나 (그 노드의 왼쪽 자식 노드가 root인 트리를 중위 순회한 결과) (root 노드) (그 노드의 오른쪽 자식 노드가 root인 트리를 중위 순회한 결과)이다. + +이진 트리를 복원할 때는 분할 정복을 사용한다. +전위 순회 결과를 통해 트리의 root 노드를 찾은 다음, 그 root 노드를 중위 순회 결과에서 찾으면, 그 노드를 중심으로 왼쪽에 위치한 모든 노드와 오른쪽에 위치한 모든 노드를 찾을 수 있다. +전위 순회 결과에서 root 노드 이후에는 그 노드를 중심으로 왼쪽에 위치한 모든 노드가 나오고 그 뒤에 오른쪽에 위치한 모든 노드가 나온다. +따라서 중위 순위 결과에서 찾을 수 있는 왼쪽에 위치한 모든 노드를 대상으로, 왼쪽의 이진 트리를 복원하고, +그 후에 오른쪽에 위치한 모든 노드를 대상으로 오른쪽의 이진 트리를 복원하는 과정을 반복할 것이다. +즉 root 노드를 찾은 후에, 그 root 노드의 왼쪽 자식을 root로 하는 이진 트리를 만들고, 오른쪽 자식을 root로 하는 이진 트리를 만드는 것이다. +전체의 이진 트리를 만드는 것이 기본 문제(problem)이고, root 노드의 왼쪽 또는 오른쪽 자식 노드를 root로 하는 이진 트리를 만드는 것이 하위 문제(sub problem)인 것이다. + +이 과정에서 전위 순회 결과는 앞에서부터 순서대로 출력되므로, queue를 사용하기에 알맞으며, +중위 순회 결과는 하위 문제로 갈 때, root 노드를 기준으로 왼쪽 부분과 오른쪽 부분으로 분할되게 된다. + +이진 트리가 전부 만들어지면, 이제 이를 후위 순회하며 그 결과를 출력한다. +후위 순회는 root 노드에서 먼저 왼쪽 자식 노드를 root로 하는 트리에서 후위 순회를 하고, 이후에 오른쪽 자식 노드를 root로 하는 트리에서 후위 순회를 한 다음에 root 노드를 출력한다. + + + +### 구현 배경 지식 +- 트리와 이진 트리 +- 이진 트리의 순회 방법 + - preorder : root -> left -> right + - inorder : left -> root -> right + - postorder : left -> right -> root + +- 분할 정복 divide and conquer + +### 문제를 해결한 코드 +~~~java +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Scanner; + +public class Main { + + private static Queue preorder; + private static ArrayList inorder; + private static int size; + + public static void main(String args[]) { + Scanner sc = new Scanner(System.in); + int testcase = sc.nextInt(); + + for (int t=0; t(); + inorder = new ArrayList<>(); + for (int i=0; i in1) { + Node left = makeTree(in1, index); + node.left = left; + } + if (index < in2-1) { + Node right = makeTree(index+1, in2); + node.right = right; + } + + return node; + } + + private static void printPostOrder(Node n) { + if (n == null) return; + if (n.left != null) { + printPostOrder(n.left); + } + if (n.right != null) { + printPostOrder(n.right); + } + System.out.print(n.index+" "); + } +} + +class Node { + public Node left; + public Node right; + public int index; + + public Node(int n) { + index = n; left = null; right = null; + } + + @Override + public String toString() { + int l = (left==null)? 0 : left.index; + int r = (right==null)? 0 : right.index; + return "Node{" + index+" : "+l+" "+r+"}"; + } +} +~~~ \ No newline at end of file diff --git a/source/diane/[baekjoon]6549-2.md b/source/diane/[baekjoon]6549-2.md new file mode 100644 index 0000000..cc775fb --- /dev/null +++ b/source/diane/[baekjoon]6549-2.md @@ -0,0 +1,181 @@ +# Baekjoon 히스토그램에서 가장 큰 직사각형 + +- 히스토그램에서 가장 큰 직사각형 + - https://www.acmicpc.net/problem/6549 + +### 문제 이해하기 +히스토그램은 직사각형 여러 개가 아래쪽으로 정렬되어 있는 도형으로, 이 직사각형은 모두 같은 너비를 가지고 있지만 높이는 서로 다르다. +이 때 이 히스토그램에서 그릴 수 있는 직사각형 중 가장 넓이가 큰 직사각형의 넓이를 구하여라. + +#### 입력 +- int N A1 A2 ... AN : 직사각형의 수와 높이 (여러 벌) + - N : 직사각형의 갯수 + - A1 ... AN : 직사각형의 높이 +- 0 : 입력의 끝 + +#### 출력 +- int : 히스토그램에서 가장 넓이가 큰 직사각형의 넓이 (입력의 수 만큼) + +### 문제 접근 방법 +먼저 주어진 히스토그램으로 segment tree를 만든다. +이 때 각 노드는 히스토그램에서 그 노드의 구간만을 보았을 때, 그 너비로 만들 수 있는 가장 큰 직사각형의 정보를 담을 것이다. +따라서, 그 너비(number)와 가장 큰 직사각형의 높이(height), 그리고 이 높이를 가지는 직사각형의 인덱스(index) 정보를 가진다. +두 노드의 부모 노드는, 이 두 노드가 가진 구간에서 그 너비로 만들 수 있는 가장 큰 직사각형의 정보를 담아야 하기 때문에, +너비는 자식 노드 둘의 너비의 합이고, 높이는 두 자식 노드가 가진 높이 중에 작은 것, 인덱스는 더 작은 높이를 가진 자식 노드가 가졌던 인덱스를 가진다. + +segment tree가 다 만들어지면, 이를 이용해서 전체 히스토그램에서의 가장 큰 직사각형을 찾을 것이다. +가장 간단한 방법은 완전 탐색이지만, 이는 O(N^2)의 시간 복잡도를 가지므로 주어진 시간 안에 찾지 못할 수도 있다. +따라서 Divide and Conquer 방식을 사용한다. +먼저 전체 너비로 만들 수 있는 가장 큰 직사각형의 넓이를 구한다. +이 직사각형의 높이는 이 너비에 있는 각 직사각형의 높이 중 가장 작은 것이다. +이 가장 작은 직사각형을 중심으로 그 왼쪽 구간과 오른쪽 구간으로 나누어 그 넓이를 구하고, +또 이 때 가장 작은 직사각형을 찾아 구간을 나누어 넓이를 구하는 과정을 반복한다. +가장 작은 높이가 포함되어 있다면, 현재 구한 너비가 가장 큰 너비이기 때문에, 이 높이의 직사각형을 제하고 양 옆으로 구간을 나누는 것이다. +이렇게 구한 직사각형의 넓이 중 가장 큰 것이, 전체 히스토그램에서 그릴 수 있는 가장 넓이가 큰 직사각형이다. + + +### 구현 배경 지식 +- Segment Tree + Divide and Conquer + +### 문제를 해결한 코드 +~~~java +import java.util.Scanner; + +public class Main { + private static long[] array; + private static Node[] segTree; + + public static void main(String args[]) { + Scanner sc = new Scanner(System.in); + String testCase = null; + + while (true) { + testCase = sc.nextLine(); + if (testCase.equals("0")) break; + + array = convert(testCase); + System.out.println(solution()); + } + + } + + private static long solution() { + segTree = makeSegTree(); + init(1,0,array.length-1); + +/* 완전 탐색으로 접근하여 시간 초과가 난 풀이 + int maxArea = Integer.MIN_VALUE; + for (int i=array.length-1; i>0; i--) { + for (int j=0; j maxArea) maxArea = area; + } + } +*/ + return getBiggerArea(0, array.length-1); + } + + private static Node[] makeSegTree() { + double height = Math.ceil(Math.log(array.length)/Math.log(2.0)); + int treeSize = (int) Math.pow(2.0, height+1); + return new Node[treeSize]; + } + + private static Node init(int node, int start, int end) { + if (start == end) return segTree[node] = new Node(array[start], 1, start); + int mid = (start+end)/2; + Node n1 = init(node*2, start, mid); + Node n2 = init(node*2+1, mid+1, end); + long height = n1.height; + int index = n1.index; + if (n1.height > n2.height) { + height = n2.height; index = n2.index; + } + return segTree[node] = new Node(height, n1.number+n2.number, index); + } + + private static long getBiggerArea(int start, int end) { + Node node = findArea(1,0, array.length-1, start, end); + long area = node.area; + if (node.index == -1) return area; + if (start < node.index) { + long temp = getBiggerArea(start, node.index-1); + if (temp > area) area = temp; + } + if (end > node.index) { + long temp = getBiggerArea(node.index+1, end); + if (temp > area) area = temp; + } + return area; + } + + private static Node findArea(int node, int start, int end, int left, int right) { + if (left > end || right < start) return new Node(0,0, -1); + if (left <= start && end <= right) { + return segTree[node]==null? new Node(0,0, -1):segTree[node]; + } + int mid = (start+end)/2; + Node n1 = findArea(node*2, start, mid, left, right); + Node n2 = findArea(node*2+1, mid+1, end, left, right); + long height = n1.height; + int index = n1.index; + if (n1.height == 0) { + if (n2.height == 0) { + height = 0; + if (n1.index == -1) index = n2.index; + else if (n2.index == -1) index = n1.index; + else index = (n1.index>n2.index)?n2.index:n1.index; + } else if (n1.index == -1){ + height = n2.height; index = n2.index; + } else { + height = n1.height; index = n1.index; + } + } else if (n2.index == -1) { + height = n1.height; index = n1.index; + } else if (n2.height == 0) { + height = n2.height; index = n2.index; + } else if (n1.height > n2.height){ + height = n2.height; index = n2.index; + } + return new Node(height, n1.number+n2.number, index); + } + + private static long[] convert(String array) { + String[] temp = array.split(" "); + long[] result = new long[temp.length-1]; + for (int i=1; i stack = new Stack<>(); // stack에는 현재보다 작은 것들만 남아있을 것 + int size = array.length; + + long area = Long.MIN_VALUE; + for (int i=0; iheight) { + Element oldElm = stack.pop(); + int width = (stack.empty()? i: i-stack.peek().index-1); + long newArea = width*oldElm.height; + if (area < newArea) area = newArea; + } + stack.push(new Element(height, i)); + } + + while(!stack.empty()) { + Element temp = stack.pop(); + int width = (stack.empty()? size: size-stack.peek().index-1); + long newArea = width*temp.height; + if (area < newArea) area = newArea; + } + + System.out.println(area); + } + + } + + private static long[] convert(String array) { + String[] temp = array.split(" "); + long[] result = new long[temp.length-1]; + for (int i=1; i> networks = new ArrayList<>(); + + while (checkFlag()) { + int start = getFirstRemainder(); + System.out.println(start); + Set network = dfs(start); + networks.add(network); + } + return networks.size(); + } + + private boolean checkFlag() { + boolean result = true; + int idx = 0; + while (result) { + if (idx >= flags.length) break; + result = result && flags[idx]; + idx++; + } + return !result; + } + + private int getFirstRemainder() { + for (int i=0; i dfs(int start) { + Set network = new HashSet<>(); + + flags[start] = true; + for (int i=0; i> disks; + private MinHeap remainings; + + public Solution() { + disks = new HashMap<>(); + remainings = new MinHeap(); + } + + public int solution(int[][] jobs) { + int time = 0; + int runtime = 0; + int number = jobs.length; + + for (int[] disk : jobs) { + ArrayList list = new ArrayList<>(); + if (disks.containsKey(disk[0])) { + list = disks.get(disk[0]); + } + list.add(Arrays.stream(disk).boxed().toArray(Integer[]::new)); + disks.put(disk[0], list); + } + + ArrayList times = new ArrayList<>(disks.keySet()); + Collections.sort(times); + while (times.size() > 0 || remainings.size() > 0) { + // 이 시간에 사용할 수 있는 disk 넣 + while (times.size() > 0 && times.get(0) <= time) { + for (Integer[] item : disks.get(times.get(0))) { + remainings.insert(item); + } + times.remove(0); + if (times.size() < 1) break; + } + + // 만약 이 시간에 할 수 있는 것이 없다면 + if (remainings.size()<1) { + time++; + continue; + } + + Integer[] item = remainings.delete(); + time = time + item[1]; + runtime = runtime + (time-item[0]); + } + + System.out.println("Total Time : "+time); + runtime = runtime / number; + + return runtime; + } +} + +class MinHeap { + private ArrayList heap; + + public MinHeap() { + heap = new ArrayList<>(); + heap.add(new Integer[] {0,0}); // index 0 added (not use index 0) + } + + public void insert(Integer[] v) { +// Integer[] value = Arrays.stream(v).boxed().toArray(Integer[]::new); + heap.add(v); + + int p = heap.size()-1; + while (p>1 && heap.get(p/2)[1]>heap.get(p)[1]) { + Integer[] temp = heap.get(p/2); + heap.set(p/2, heap.get(p)); + heap.set(p, temp); + p = p/2; + } + } + + public Integer[] delete() { + if (heap.size() < 2) { + return new Integer[] {0,0}; + } + + Integer[] deleteItem = heap.get(1); + heap.set(1, heap.get(heap.size()-1)); + heap.remove(heap.size()-1); + + int p = 1; + while ((p*2) < heap.size()) { + Integer[] min = heap.get(p*2); + int minp = p*2; + if ((minp+1 < heap.size()) && (min[1] > heap.get(minp+1)[1])) { + min = heap.get(minp+1); + minp = minp+1; + } + if (heap.get(p)[1] < min[1]) break; + + heap.set(minp, heap.get(p)); + heap.set(p, min); + p = minp; + } + + return deleteItem; + } + + public int size() { + return heap.size()-1; + } + + public String toString() { + String line = ""; + return line; + } +} +~~~ + diff --git a/source/diane/[programmers]42628.md b/source/diane/[programmers]42628.md new file mode 100644 index 0000000..5b61947 --- /dev/null +++ b/source/diane/[programmers]42628.md @@ -0,0 +1,85 @@ +# Programmers 이중우선순위큐 문제 + +- 이중우선순위큐 + - https://programmers.co.kr/learn/courses/30/lessons/42628 + +### 문제 이해하기 +이중우선순위큐는 연산의 목록을 받는다. +그 연산은 세 가지 종류로, 큐에 숫자를 추가하거나, 큐에서 최댓값 또는 최솟값을 제거한다. +이중우선순위큐가 이 연산을 모두 실행한 후의 큐의 최댓값과 최솟값을, 큐가 비어있다면 0,0을 반환한다. +모든 연산은 숫자 하나만을 대상으로 하고, 만약 큐가 비어있는데 값을 삭제해야한다면, 그 연산은 무시한다. + +#### 입력 +- String[] operations : 연산의 목록 + - 각 연산은 첫 글자로 알파벳 I 또는 D가 오고, 띄어쓰기 후 I라면 모든 숫자, D라면 1 또는 -1이 온다. + - 연산의 의미 + - I 숫자 : 큐에 이 숫자를 추가 + - D 1 : 큐에서 최댓값 삭제 + - D -1 : 큐에서 최솟값 삭제 + +#### 출력 +- int[] : 크기 2의 숫자 배열로, 첫 원소는 이 큐의 최댓값, 마지막 원소는 이 큐의 최소값. + - 만약 큐가 비어있다면, [0,0]을 반환. + +### 문제 접근 방법 +"이중우선순위큐"이지만, FIFO과는 관계가 없다. +또한 원소들의 순서는 전혀 관계없고, 그때그때 최솟값과 최댓값만 알면 되기 때문에, +자료구조로 minHeap과 maxHeap을 동시에 사용했다. + +모든 연산에 대해 순서대로 아래 행동을 반복한다. +- I 숫자 : minHeap과 maxHeap에 이 숫자를 추가한다. +- D 1 : maxHeap에서 최댓값을 제거하고, minHeap에서 동일한 값을 제거한다. +- D -1 : minHeap에서 최솟값을 제거하고, maxHeap에서 동일한 값을 제거한다. +모든 연산을 수행했다면, maxHeap에서 최댓값을, minHeap에서 최솟값을 찾아 반환한다. +만약 힙의 크기가 0이라면, 최댓값과 최솟값으로 0을 반환한다. + +### 구현 배경 지식 +MinHeap, MaxHeap + +### 문제를 해결한 코드 +~~~java +import java.util.Collections; +import java.util.PriorityQueue; + +public class Solution { + PriorityQueue minHeap; + PriorityQueue maxHeap; + + public Solution() { + minHeap = new PriorityQueue<>(); + maxHeap = new PriorityQueue<>(Collections.reverseOrder()); + } + + public int[] solution(String[] operations) throws Exception { + int[] answer = new int[2]; + + for (String operation : operations) { + String[] commands = operation.split(" "); + if (commands[0].equals("I")) { + int number = Integer.parseInt(commands[1]); + minHeap.add(number); + maxHeap.add(number); + } else if (commands[0].equals("D")) { + if (minHeap.size() < 1) continue; + if (commands[1].equals("1")) { + int number = maxHeap.poll(); + minHeap.remove(number); + } else if (commands[1].equals("-1")) { + int number = minHeap.poll(); + maxHeap.remove(number); + } else { + throw new Exception("Invalid operation : "+operation); + } + } else { + throw new Exception("Invalid operation : "+operation); + } + } + if (maxHeap.size() != 0) { + answer[0] = maxHeap.peek(); + answer[1] = minHeap.peek(); + } + + return answer; + } +} +~~~ \ No newline at end of file diff --git a/source/diane/[programmers]42862.md b/source/diane/[programmers]42862.md new file mode 100644 index 0000000..a07a970 --- /dev/null +++ b/source/diane/[programmers]42862.md @@ -0,0 +1,64 @@ +# Programmers 체육복 문제 + +- 체육복 + - https://programmers.co.kr/learn/courses/30/lessons/42862 + +### 문제 이해하기 +학생들은 기본으로 체육복을 하나씩 가지고 있다. 그런데 점심시간에 도둑이 들어, 일부 학생이 체육복을 도난 당했다. +따라서 여벌 체육복을 가진 학생들이 체육복이 없는 학생에게 체육복을 빌려주려고 한다. +여벌 체육복이 없는 학생은 자신의 체육복을 입어야 하기 때문에, 체육복을 빌려줄 수 없으며, 여벌의 체육복이 있었더라도 자신의 체육복을 도난 당했다면 자신의 여벌 체육복을 빌려줄 수 없다. +학생들은 모두 체격이 다르기 때문에, 체격 순으로 매겨진 번호로 자신의 바로 앞번호나 바로 뒷번호의 학생의 체육복만 입을 수 있다. +가능한 많은 학생들이 체육복을 입게 한다면, 총 몇 명의 학생이 체육복을 입을 수 있는지를 구하여라. + +#### 입력 +- int n : 전체 학생의 수 (번호는 1번부터 n번까지, 중복되지 않는다.) +- int[] lost : 체육복을 도난당한 학생들의 번호가 담긴 배열 (중복 없음) +- int[] reserve : 여벌의 체육복이 있는 학생들의 번호가 담긴 배열 (중복 없음) + +#### 출력 +- int : 체육복을 입을 수 있는 가장 많은 학생 수 + +### 문제 접근 방법 +체육복이 없는 학생의 번호를 작은 순부터 탐색하면서, +여벌의 체육복을 가진 학생이 앞번호에 있으면 그 학생에게, 없으면 그 뒷학생에게 체육복을 빌린다고 가정한다. +만약 양쪽 번호 모두 여벌의 체육복이 없으면 체육복이 없는 학생은 체육복을 빌리지 못한다. + +여벌의 체육복이 있더라도, 자신의 체육복을 도난당한 학생은 체육복을 빌려줄 수 없기 떄문에, +이 경우는 사전에 제거한다. + +### 구현 배경 지식 +Greedy Algorithm + +### 문제를 해결한 코드 +~~~java +import java.util.ArrayList; + +public class Solution { + public int solution(int n, int[] lost, int[] reserve) { + int answer = n - lost.length; + ArrayList more = new ArrayList<>(); + ArrayList no = new ArrayList<>(); + + for (int i : reserve) more.add(i); + for (int i : lost) { + if (more.contains(i)) { + more.remove((Integer)i); + answer++; + } else { + no.add(i); + } + } + for (int i : no) { + if (more.contains(i-1)) { + more.remove((Integer)(i-1)); + answer++; + } else if (more.contains(i+1)) { + more.remove((Integer)(i+1)); + answer++; + } + } + + return answer; + } +} +~~~ diff --git a/source/diane/[programmers]43164.md b/source/diane/[programmers]43164.md new file mode 100644 index 0000000..975d8eb --- /dev/null +++ b/source/diane/[programmers]43164.md @@ -0,0 +1,109 @@ +# Programmers 여행경로 문제 + +- 여행경로 + - https://programmers.co.kr/learn/courses/30/lessons/43164 + +### 문제 이해하기 +출발 도시와 도착 도시가 고정된 항공권 목록이 주어진다. +이 항공권을 모두 이용하여 여행경로를 짜야한다. +첫 출발지는 "ICN"이며, 항공권을 모두 사용할 수 없는 경우는 주어지지 않는다. +만약 가능한 여행경로가 여러 개라면, 알파벳 순으로 앞서는 경로를 구한다. + +#### 입력 +- String[][] tickets : 항공권의 목록 + - 모든 ticket은 길이 2의 String 배열로 이루어져 있다 + - ticket의 첫번째 원소는 출발지, 두번째 원소는 도착지이다 + +#### 출력 +- String[] : "ICN"으로부터 시작하는 여행 경로의 도시 목록 + +### 문제 접근 방법 +모든 가능성을 전부 탐색하면서 항공권을 모두 사용하는 여행경로를 찾는다. +그 방법은 다음과 같다. +1. 현재 위치한 도시를 찾는다. (처음은 ICN) +2. 현재 위치한 도시에서 갈 수 있는 모든 도시를 찾는다. +3. 각 다음 도시에 대해 그 도시를 현재의 도시로 하여 이 전체 과정을 반복한다. 즉, 그 도시로 이동할 경우 가능한 여행경로 중 가장 좋은 경로를 찾는다. +4. 과정 3에서 찾은 각 다음 도시의 가장 좋은 여행경로 중 조건에 맞는 여행경로를 찾는다. + +여기서 조건이 맞다는 것은 아래와 같다. +1. 이 도시에서 시작하는 여행경로 중 가장 긴 경로일 것. +한 여행경로보다 더 긴 여행경로가 있다는 것은, 그 여행경로가 충분히 많은 도시를 여행하지 않았다는 뜻이다. +또한, 문제의 조건에서 모든 도시를 여행하지 못하는 경우는 없다고 했기 때문에 (즉, 그런 여행경로가 없는 경우를 판단할 필요가 없기 때문에), 모든 항공권을 다 사용한 여행경로는 필연적으로 가장 길 것이다. +2. 만약 여러 여행경로의 길이가 같다면, 이 중 알파벳 순으로 앞서는 것을 선택한다. + +### 구현 배경 지식 +DFS, Backtracking + +### 문제를 해결한 코드 +~~~java +import java.util.ArrayList; +import java.util.HashMap; + +public class Solution { + + public String[] solution(String[][] tickets) { + // tickets 정보를 HashMap에 넣는다 + HashMap> list = new HashMap<>(); + for (String[] ticket : tickets) { + ArrayList dest = new ArrayList<>(); + if (list.containsKey(ticket[0])) { + dest = list.get(ticket[0]); + } + dest.add(ticket[1]); + list.put(ticket[0], dest); + } + + // ICN을 시작으로 여행경로 찾기 + ArrayList path = new ArrayList<>(); + path.add("ICN"); + path = findPath(path, list); + + return path.toArray(new String[path.size()]); + } + + private ArrayList findPath(ArrayList path, HashMap> list) { + ArrayList finalpath = path; + String depart = path.get(path.size()-1); + + // 이 다음으로 갈 수 있는 모든 목적지에 대해서 경로 탐색하기 + if (list.get(depart) == null) return path; + for (String dest : list.get(depart)) { + // 사용한 티켓을 제외한 list 만들기 + HashMap> newlist = new HashMap<>(list); + ArrayList temp = new ArrayList<>(list.get(depart)); + temp.remove(dest); + if (temp.size() == 0) { + newlist.remove(depart); + } else { + newlist.put(depart, temp); + } + + // 새로운 목적지로 가는 path 만들기 + temp = new ArrayList<>(path); + temp.add(dest); + + // 그 결과! + temp = findPath(temp, newlist); + finalpath = comparePath(temp, finalpath); + } + + return finalpath; + } + + private ArrayList comparePath(ArrayList path1, ArrayList path2) { + // 더 긴 경로가 정답일 것 + if (path1.size() < path2.size()) return path2; + if (path1.size() > path2.size()) return path1; + + // 길이가 같다면, 알파벳 순으로 먼저 오는 것이 정답일 것 + ArrayList answer = path1; + for (int i=0; i 0) answer = path2; + break; + } + return answer; + } +} +~~~ diff --git a/source/diane/[programmers]60059.md b/source/diane/[programmers]60059.md new file mode 100644 index 0000000..8204e04 --- /dev/null +++ b/source/diane/[programmers]60059.md @@ -0,0 +1,132 @@ +# Programmers 자물쇠와 열쇠 문제 + +- 자물쇠와 열쇠 + - https://programmers.co.kr/learn/courses/30/lessons/60059 + +### 문제 이해하기 +NxN 크기의 자물쇠와 MxM 크기의 열쇠가 있다. +자물쇠의 모든 홈에 열쇠의 돌기가 들어가고, 자물쇠의 돌기와 열쇠의 돌기가 만나지 않는 경우, 그 열쇠로 자물쇠를 열 수 있다. +이 때 열쇠는 회전하고 이동할 수 있다. +하나의 열쇠와 하나의 자물쇠가 주어질 때, 이 열쇠로 자물쇠를 열 수 있는지 여부를 반환하라. +M은 항상 N 이하이고, M과 N은 3 이상 20 이하인 자연수이다. + +#### 입력 +- int[][] key : 열쇠의 모양, 0은 홈 부분, 1은 돌기 부분. +- int[][] lock : 자물쇠의 모양, 0은 홈 부분, 1은 돌기 부분. + +#### 출력 +- boolean : 주어진 열쇠로 자물쇠를 열 수 있는지 여부 + +### 문제 접근 방법 +열쇠로 자물쇠를 열 수 있는 조건은 아래와 같다. +1. 자물쇠의 모든 홈에 열쇠의 돌기가 있을 것 +2. 자물쇠의 돌기와 열쇠의 돌기가 만나지 않을 것 + +자물쇠의 홈 부분이 없는 경우는 이미 자물쇠가 열린 상태이므로 true를 반환하고, 열쇠의 돌기의 수가 자물쇠의 홈의 수보다 작은 경우는 무슨 수를 써도 열쇠로 자물쇠를 열 수 없으므로 false를 반환한다. + +자물쇠의 홈 중에 하나를 선택하여, 열쇠의 모든 돌기를 차례대로 그 홈에 맞추어보고, 그 때 자물쇠의 모든 홈을 열쇠의 돌기로 채울 수 있는지 확인한다 (1번 조건). +만약 이 조건을 만족한다면, 이제 열쇠의 모든 돌기에 대해서 자물쇠의 홈에 들어가지 않고 자물쇠의 돌기와 만나는 부분이 없는지 확인한다(2번 조건). +두 조건을 모두 만족하면, 열쇠로 자물쇠를 열 수 있으므로 true를 반환하고, +그렇지 않다면 열쇠를 90도씩 돌려가며 위의 과정을 반복하여 자물쇠를 열어본다. +열쇠를 90도씩 돌려가며 모든 방향에 대해 확인했는데도 자물쇠를 열 수 없다면 false를 반환한다. + + +### 문제를 해결한 코드 +~~~java +import java.util.ArrayList; + +public class Solution { + private ArrayList locks; + private ArrayList keys; + + public boolean solution(int[][] key, int[][] lock) { + boolean answer = true; + + locks = makeList(lock, 0); + keys = makeList(key, 1); + + if (locks.size() == 0) return true; // 이미 열려있는 경우 + if (locks.size() > keys.size()) return false; // 답에 영향이 없네 + + // locks의 모든 것이 keys에 있어야 함 + int size = keys.size(); + for (int rotate=0; rotate<4; rotate++) { + for (int i = 0; i < size; i++) { + answer = true; + int xdiff = keys.get(i).x-locks.get(0).x; + int ydiff = keys.get(i).y-locks.get(0).y; + for (Pair l : locks) { + Pair temp = new Pair(l.x+xdiff, l.y+ydiff); + if (!keys.contains(temp)) { + answer = false; + break; + } + } + if (answer && NotExistConflict(lock.length, xdiff, ydiff)) { + return true; + } + } + keys = rotateRight(keys, key.length); + } + + return answer; + } + + private boolean NotExistConflict(int n, int xdiff, int ydiff) { + if (locks.size() == keys.size()) return true; + // key의 튀어나온 부분과 lock의 튀어나온 부분이 충돌하는 경우 + for (Pair key : keys) { + Pair temp = new Pair(key.x-xdiff, key.y-ydiff); + if (!locks.contains(temp)) { + if (temp.x >=0 && temp.x < n && temp.y >=0 && temp.y < n) + return false; + } + } + return true; + } + + private ArrayList makeList(int[][] rawData, int flag) { + ArrayList result = new ArrayList<>(); + int length = rawData.length; + for (int i=0; i rotateRight(ArrayList list, int length) { + ArrayList result = new ArrayList<>(); + for (Pair pair : list) { + result.add(new Pair(pair.y, length-pair.x)); + } + return result; + } + +} + +class Pair extends Object { + public int x; + public int y; + + public Pair(int x, int y) { + this.x = x; this.y = y; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass()!=o.getClass()) return false; + Pair p = (Pair) o; + return (x==p.x)&&(y==p.y); + } + + @Override + public int hashCode() { + return x*100+y; + } +} +~~~ + diff --git a/source/eunbee/.DS_Store b/source/eunbee/.DS_Store new file mode 100644 index 0000000..ba488e3 Binary files /dev/null and b/source/eunbee/.DS_Store differ diff --git a/source/eunbee/.gitignore b/source/eunbee/.gitignore new file mode 100644 index 0000000..e10e727 --- /dev/null +++ b/source/eunbee/.gitignore @@ -0,0 +1 @@ +/.metadata/ diff --git a/source/eunbee/boj/.DS_Store b/source/eunbee/boj/.DS_Store new file mode 100644 index 0000000..4bf7b64 Binary files /dev/null and b/source/eunbee/boj/.DS_Store differ diff --git a/source/eunbee/boj/.classpath b/source/eunbee/boj/.classpath new file mode 100644 index 0000000..1cd9d95 --- /dev/null +++ b/source/eunbee/boj/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/source/eunbee/boj/.gitignore b/source/eunbee/boj/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/source/eunbee/boj/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/source/eunbee/boj/.project b/source/eunbee/boj/.project new file mode 100644 index 0000000..79a2957 --- /dev/null +++ b/source/eunbee/boj/.project @@ -0,0 +1,17 @@ + + + boj + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/source/eunbee/boj/.settings/org.eclipse.jdt.core.prefs b/source/eunbee/boj/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..910a770 --- /dev/null +++ b/source/eunbee/boj/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,14 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=14 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=14 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=14 diff --git a/source/eunbee/boj/src/.DS_Store b/source/eunbee/boj/src/.DS_Store new file mode 100644 index 0000000..478d1bc Binary files /dev/null and b/source/eunbee/boj/src/.DS_Store differ diff --git a/source/eunbee/boj/src/boj/boj_2583.java b/source/eunbee/boj/src/boj/boj_2583.java new file mode 100644 index 0000000..a9c1fec --- /dev/null +++ b/source/eunbee/boj/src/boj/boj_2583.java @@ -0,0 +1,85 @@ +package boj; +import java.util.*; + +public class boj_2583 { + public static int n, m, k; + public static int[][] graph = new int[100][100]; + //public static int[] size = new int[100]; + public static ArrayList size= new ArrayList(); + public static int check = 0; + + public static boolean dfs(int x, int y) { + if(x<0 || x >=m || y<0 || y >= n) { + return false; + } + if(graph[x][y] == 0) { + graph[x][y] = 1; + //상 + dfs(x-1,y); + //하 + dfs(x+1,y); + //좌 + dfs(x,y-1); + // + dfs(x,y+1); + check++; + return true; + } + + return false; + } + + public static void main(String[] args) { + // TODO Auto-generated method stub + Scanner sc = new Scanner(System.in); + m = sc.nextInt(); + n = sc.nextInt(); + k = sc.nextInt(); + sc.nextLine(); + + for(int i=0;i chickens = new ArrayList(); + ArrayList homes = new ArrayList(); + + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + int position = in.nextInt(); + + if (position == CHICKEN) { + chickens.add(new Point(i, j)); + } else if (position == HOME) { + homes.add(new Point(i, j)); + } + } + } + + for (int i = 0; i < chickens.size(); i++) { + boolean check[] = new boolean[chickens.size()]; + check[i] = true; + findAll(mat, homes, chickens, i, 1, chickenNum, check); + } + + System.out.println(min); + } + + public static void findAll(int mat[][], ArrayList homes, ArrayList chickens, int index, int num, + int chickenNum, boolean[] check) { + if (num == chickenNum) { + min = Math.min(min, calcChickenDistance(mat, homes, chickens, check)); + return; + } + if (index == chickens.size()) { + return; + } + + for (int i = index + 1; i < chickens.size(); i++) { + check[i] = true; + findAll(mat, homes, chickens, i, num + 1, chickenNum, check); + check[i] = false; + } + } + + public static int calcChickenDistance(int mat[][], ArrayList homes, ArrayList chickens, + boolean[] check) { + int distanceSum = 0; + for (Point home : homes) { + int distance = Integer.MAX_VALUE; + for (int i = 0; i < check.length; i++) { + if (check[i]) { + Point chicken = chickens.get(i); + distance = Math.min(distance, Math.abs(home.row - chicken.row) + Math.abs(home.col - chicken.col)); + } + } + distanceSum += distance; + } + + return distanceSum; + } +} + +class Point { + int row; + int col; + + public Point(int row, int col) { + this.row = row; + this.col = col; + } +} +``` diff --git a/source/soyeon/[boj] 17142.md b/source/soyeon/[boj] 17142.md new file mode 100644 index 0000000..6d8a275 --- /dev/null +++ b/source/soyeon/[boj] 17142.md @@ -0,0 +1,499 @@ +# 백준 문제 + +- 연구소3 + - https://www.acmicpc.net/problem/17142 + +### 문제 이해하기 +가장 처음에 모든 바이러스는 비활성 상태이고, 활성 상태인 바이러스는 상하좌우로 인접한 모든 빈 칸으로 동시에 복제되며, 1초가 걸린다. + +승원이는 연구소의 바이러스 M개를 활성 상태로 변경하려고 한다. + +0은 빈 칸, 1은 벽, 2는 바이러스의 위치이다. + +### 문제 접근 방법 +일단 바이러스를 배치 할 수 있는 모든 경우를 구한다. + +각 경우에 대해 연구소의 모든 빈 칸에 바이러스가 있게 되는 최소 시간을 구한다. + +### 구현 배경 지식 +백트래킹, bfs + +### 접근 방법을 적용한 코드 +```java +import java.util.*; + +public class Main { + public static int min = Integer.MAX_VALUE; + public static int SPACE = 0; + public static int WALL = 1; + public static int VIRUS = 2; + public static int SLEEP_VIRUS = 3; + public static int[] dr = { 1, -1, 0, 0 }; + public static int[] dc = { 0, 0, 1, -1 }; + public static HashSet virusPositionSet; + + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + int size = in.nextInt(); + int virusNum = in.nextInt(); + + virusPositionSet = new HashSet(); + + int mat[][] = new int[size][size]; + LinkedList viruses = new LinkedList(); + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + mat[i][j] = in.nextInt(); + if (mat[i][j] == VIRUS) { + viruses.add(new Point(i, j)); + } + } + } + + for (int i = 0; i < viruses.size(); i++) { + int check[] = new int[viruses.size()]; + check[i] = 1; + findVirusPosition(viruses, check, i + 1, 1, mat, virusNum); + } + + for (int[] check : virusPositionSet) { + int spreadTime = findTimeVirusSpread(viruses, check, mat); + min = Math.min(min, spreadTime); + } + + if (min == Integer.MAX_VALUE) { + System.out.println("-1"); + } else { + System.out.println(min); + } + + } + + public static void findVirusPosition(LinkedList viruses, int check[], int index, int size, int mat[][], + int virusNum) { + if (size == virusNum) { + virusPositionSet.add(copyCheck(check)); + return; + } + + if (index == check.length) { + return; + } + + for (int i = index; i < check.length; i++) { + check[i] = 1; + findVirusPosition(viruses, check, i + 1, size + 1, mat, virusNum); + check[i] = 0; + } + } + + public static int[] copyCheck(int[] check) { + int[] newCheck = new int[check.length]; + for (int i = 0; i < newCheck.length; i++) { + newCheck[i] = check[i]; + } + return newCheck; + } + + public static int findTimeVirusSpread(LinkedList viruses, int check[], int mat[][]) { + int[][] newMat = copyMat(mat); + + Queue virusPosition = new LinkedList(); + for (int i = 0; i < check.length; i++) { + if (check[i] == 1) { + Point point = viruses.get(i); + newMat[point.row][point.col] = VIRUS; + virusPosition.add(Point.builder(point)); + } else { + Point point = viruses.get(i); + newMat[point.row][point.col] = SLEEP_VIRUS; + } + } + + return bfsSpread(newMat, virusPosition); + } + + public static int bfsSpread(int mat[][], Queue viruses) { + //System.out.println("--------------------"); + int time = 0; + int size = mat.length; + + while (!viruses.isEmpty()) { + //System.out.println(viruses); + //printMat(mat); + if (isFull(mat)) { + return time; + } + + HashSet nextViruses = new HashSet(); + + while (!viruses.isEmpty()) { + Point point = viruses.poll(); + for (int i = 0; i < 4; i++) { + int newRow = point.row + dr[i]; + int newCol = point.col + dc[i]; + if (isBoundary(newRow, newCol, size)) { + if (mat[newRow][newCol] == SPACE) { + nextViruses.add(new Point(newRow, newCol)); + mat[newRow][newCol] = VIRUS; + } else if (mat[newRow][newCol] == SLEEP_VIRUS) { + nextViruses.add(new Point(newRow, newCol)); + mat[newRow][newCol] = VIRUS; + } + } + } + } + + viruses.addAll(nextViruses); + time++; + // printMat(mat); + } + + if (!isFull(mat)) { + return Integer.MAX_VALUE; + } + + return time; + } + + public static void printMat(int mat[][]) { + int size = mat.length; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + System.out.print(mat[i][j] + " "); + } + System.out.println(); + } + System.out.println(); + } + + public static boolean isFull(int mat[][]) { + int size = mat.length; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + if (mat[i][j] == SPACE) { + return false; + } + } + } + return true; + } + + public static boolean isBoundary(int row, int col, int size) { + if (row < 0) { + return false; + } + if (col < 0) { + return false; + } + if (row >= size) { + return false; + } + if (col >= size) { + return false; + } + return true; + } + + public static int[][] copyMat(int mat[][]) { + int size = mat.length; + int newMat[][] = new int[size][size]; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + if (mat[i][j] != VIRUS) { + newMat[i][j] = mat[i][j]; + } + } + } + return newMat; + } +} + +class Point { + int row; + int col; + + public Point(int row, int col) { + this.row = row; + this.col = col; + } + + public static Point builder(Point point) { + return new Point(point.row, point.col); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (this.getClass() != obj.getClass()) { + return false; + } + if (this == obj) { + return true; + } + + Point objPoint = (Point) obj; + if (this.row == objPoint.row && this.col == objPoint.col) { + return true; + } + return false; + } + + @Override + public int hashCode() { + return this.toString().hashCode(); + } + + @Override + public String toString() { + return this.row + " " + this.col; + } +} +``` + +### 해결하지 못한 이유 +시간 초과 + +### 문제를 해결한 코드 +```java +import java.util.*; + +public class Main { + public static int min = Integer.MAX_VALUE; + public static int SPACE = 0; + public static int WALL = 1; + public static int VIRUS = 2; + public static int SLEEP_VIRUS = 3; + public static int[] dr = { 1, -1, 0, 0 }; + public static int[] dc = { 0, 0, 1, -1 }; + public static HashSet virusPositionSet; + + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + int size = in.nextInt(); + int virusNum = in.nextInt(); + + virusPositionSet = new HashSet(); + + int mat[][] = new int[size][size]; + LinkedList viruses = new LinkedList(); + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + mat[i][j] = in.nextInt(); + if (mat[i][j] == VIRUS) { + viruses.add(new Point(i, j)); + } + } + } + + for (int i = 0; i < viruses.size(); i++) { + int check[] = new int[viruses.size()]; + check[i] = 1; + findVirusPosition(viruses, check, i + 1, 1, mat, virusNum); + } + + for (int[] check : virusPositionSet) { + int spreadTime = findTimeVirusSpread(viruses, check, mat); + min = Math.min(min, spreadTime); + } + + if (min == Integer.MAX_VALUE) { + System.out.println("-1"); + } else { + System.out.println(min); + } + + } + + public static void findVirusPosition(LinkedList viruses, int check[], int index, int size, int mat[][], + int virusNum) { + if (size == virusNum) { + virusPositionSet.add(copyCheck(check)); + return; + } + + if (index == check.length) { + return; + } + + for (int i = index; i < check.length; i++) { + check[i] = 1; + findVirusPosition(viruses, check, i + 1, size + 1, mat, virusNum); + check[i] = 0; + } + } + + public static int[] copyCheck(int[] check) { + int[] newCheck = new int[check.length]; + for (int i = 0; i < newCheck.length; i++) { + newCheck[i] = check[i]; + } + return newCheck; + } + + public static int findTimeVirusSpread(LinkedList viruses, int check[], int mat[][]) { + int[][] newMat = copyMat(mat); + + Queue virusPosition = new LinkedList(); + for (int i = 0; i < check.length; i++) { + if (check[i] == 1) { + Point point = viruses.get(i); + newMat[point.row][point.col] = VIRUS; + virusPosition.add(Point.builder(point)); + } else { + Point point = viruses.get(i); + newMat[point.row][point.col] = SLEEP_VIRUS; + } + } + + return bfsSpread(newMat, virusPosition); + } + + public static int bfsSpread(int mat[][], Queue viruses) { + int time = 0; + int size = mat.length; + int virusNum = calcVirusNum(mat); + + while (!viruses.isEmpty()) { + if (virusNum == 0) { + return time; + } + + HashSet nextViruses = new HashSet(); + + while (!viruses.isEmpty()) { + Point point = viruses.poll(); + for (int i = 0; i < 4; i++) { + int newRow = point.row + dr[i]; + int newCol = point.col + dc[i]; + if (isBoundary(newRow, newCol, size)) { + if (mat[newRow][newCol] == SPACE) { + nextViruses.add(new Point(newRow, newCol)); + mat[newRow][newCol] = VIRUS; + virusNum--; + } else if (mat[newRow][newCol] == SLEEP_VIRUS) { + nextViruses.add(new Point(newRow, newCol)); + mat[newRow][newCol] = VIRUS; + } + } + } + } + + viruses.addAll(nextViruses); + time++; + } + + if (virusNum != 0) { + return Integer.MAX_VALUE; + } + + return time; + } + + public static void printMat(int mat[][]) { + int size = mat.length; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + System.out.print(mat[i][j] + " "); + } + System.out.println(); + } + System.out.println(); + } + + public static int calcVirusNum(int mat[][]) { + int virusNum = 0; + int size = mat.length; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + if (mat[i][j] == SPACE) { + virusNum++; + } + } + } + return virusNum; + } + + public static boolean isBoundary(int row, int col, int size) { + if (row < 0) { + return false; + } + if (col < 0) { + return false; + } + if (row >= size) { + return false; + } + if (col >= size) { + return false; + } + return true; + } + + public static int[][] copyMat(int mat[][]) { + int size = mat.length; + int newMat[][] = new int[size][size]; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + if (mat[i][j] != VIRUS) { + newMat[i][j] = mat[i][j]; + } + } + } + return newMat; + } +} + +class Point { + int row; + int col; + + public Point(int row, int col) { + this.row = row; + this.col = col; + } + + public static Point builder(Point point) { + return new Point(point.row, point.col); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (this.getClass() != obj.getClass()) { + return false; + } + if (this == obj) { + return true; + } + + Point objPoint = (Point) obj; + if (this.row == objPoint.row && this.col == objPoint.col) { + return true; + } + return false; + } + + @Override + public int hashCode() { + return this.toString().hashCode(); + } + + @Override + public String toString() { + return this.row + " " + this.col; + } +} +``` + +### 문제를 해결한 방법 +바이러스가 다 퍼뜨려졌는지 확인할 때, matrix를 모두 체크하지 말고, 맨 첨에 바이러스 수만 구해준 다음 바이러스 수를 줄여서 0이 될 때를 확인한다. + + + diff --git a/source/soyeon/[boj] 19236.md b/source/soyeon/[boj] 19236.md new file mode 100644 index 0000000..a110803 --- /dev/null +++ b/source/soyeon/[boj] 19236.md @@ -0,0 +1,242 @@ +# 백준 문제 + +- 청소년 상어 + - https://www.acmicpc.net/problem/19236 + +### 문제 이해하기 + +상어가 이동하면서 물고기를 먹는다. + +상어가 물고기 먹음 -> 물고기 이동 -> 상어가 물고기 먹음 -> ... + +물고기는 크기 순으로 이동하며 이동할 수 없을 경우에는 돈다. + +### 문제 접근 방법 + +시뮬레이션 + +### 구현 배경 지식 + +### 접근 방법을 적용한 코드 +```java +import java.util.*; +import java.util.stream.Collectors; + +public class Main { + public static int MAX_LENGTH = 4; + public static int dr[] = { -1, -1, 0, 1, 1, 1, 0, -1 }; + public static int dc[] = { 0, -1, -1, -1, 0, 1, 1, 1 }; + public static int max = 0; + + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + + Shark shark = new Shark(); + Fish[] fishList = new Fish[17]; + int[][] fishes = new int[4][4]; + + for (int i = 0; i < MAX_LENGTH; i++) { + for (int j = 0; j < MAX_LENGTH; j++) { + int num = in.nextInt(); + int direction = in.nextInt(); + + fishes[i][j] = num; + fishList[num] = new Fish(num, direction - 1, i, j); + } + } + + // 0번 물고기 먹기 + eatFish(shark, fishList, fishes, 0, 0); + simulate(fishList, fishes, shark); + + System.out.println(max); + } + + public static void simulate(Fish[] fishList, int[][] fishes, Shark shark) { + simulateFish(fishList, fishes, shark); // 물고기를 움직인다 + + if (!isPossibleSimulation(shark, fishes)) { // 상어가 움직일 수 있는지 본다 + max = Math.max(max, shark.sum); + return; + } + + for (int i = 1; i <= 4; i++) { // 상어가 움직인다 + // 바운더리 안에 물고기가 있는 경우 + if (isBoundaryForShark(shark.row + dr[shark.direction] * i, shark.col + dc[shark.direction] * i, fishes)) { + int newRow = shark.row + dr[shark.direction] * i; + int newCol = shark.col + dc[shark.direction] * i; + + Fish[] newFishList = new Fish[fishList.length]; + for (Fish fish : fishList) { + if (fish == null) { + continue; + } + newFishList[fish.name] = Fish.builder(fish); + } + + int[][] newFishes = copyFishes(fishes); + + Shark newShark = new Shark(shark.sum, shark.direction, shark.row, shark.col); + + eatFish(newShark, newFishList, newFishes, newRow, newCol); + simulate(newFishList, newFishes, newShark); + } + } + } + + public static int[][] copyFishes(int[][] fishes) { + int[][] newFishes = new int[MAX_LENGTH][MAX_LENGTH]; + for (int i = 0; i < MAX_LENGTH; i++) { + for (int j = 0; j < MAX_LENGTH; j++) { + newFishes[i][j] = fishes[i][j]; + } + } + return newFishes; + } + + public static boolean isPossibleSimulation(Shark shark, int[][] fishes) { + for (int i = 1; i <= 4; i++) { + if (isBoundaryForShark(shark.row + dr[shark.direction] * i, shark.col + dc[shark.direction] * i, fishes)) { + return true; + } + } + return false; + } + + public static boolean isBoundaryForShark(int row, int col, int[][] fishes) { + if (row < 0) { + return false; + } + if (col < 0) { + return false; + } + if (row >= MAX_LENGTH) { + return false; + } + if (col >= MAX_LENGTH) { + return false; + } + if (fishes[row][col] == 0) { + return false; + } + return true; + } + + public static void simulateFish(Fish[] fishList, int[][] fishes, Shark shark) { + for (Fish fish : fishList) { + if (fish == null) { + continue; + } + + if (!isBoundaryForFish(fish.row + dr[fish.direction], fish.col + dc[fish.direction], shark)) { + int direction = fish.direction; + for (int i = 1; i <= 8; i++) { + direction++; + direction %= 8; + + if (isBoundaryForFish(fish.row + dr[direction], fish.col + dc[direction], shark)) { + fish.direction = direction; + break; + } + } + } + + int newRow = fish.row + dr[fish.direction]; + int newCol = fish.col + dc[fish.direction]; + + if (isBoundaryForFish(newRow, newCol, shark)) { + if (fishes[newRow][newCol] == 0) { + fishes[fish.row][fish.col] = 0; + fish.row = newRow; + fish.col = newCol; + fishes[fish.row][fish.col] = fish.name; + fishList[fish.name] = fish; + } else { + Fish nextFish = Fish.builder(fishList[fishes[newRow][newCol]]); + + int tmpRow = fish.row; + fish.row = nextFish.row; + nextFish.row = tmpRow; + + int tmpCol = fish.col; + fish.col = nextFish.col; + nextFish.col = tmpCol; + + fishes[fish.row][fish.col] = fish.name; + fishes[nextFish.row][nextFish.col] = nextFish.name; + fishList[fish.name] = fish; + fishList[nextFish.name] = nextFish; + } + } + } + } + + public static boolean isBoundaryForFish(int row, int col, Shark shark) { + if (row == shark.row && col == shark.col) { + return false; + } + if (row < 0) { + return false; + } + if (col < 0) { + return false; + } + if (row >= MAX_LENGTH) { + return false; + } + if (col >= MAX_LENGTH) { + return false; + } + return true; + } + + public static void eatFish(Shark shark, Fish[] fishList, int[][] fishes, int row, int col) { + Fish fish = fishList[fishes[row][col]]; + shark.eat(fish, row, col); + fishList[fishes[row][col]] = null; + fishes[row][col] = 0; + } +} + +class Fish { + int name; + int direction; + int row, col; + + public Fish(int name, int direction, int row, int col) { + this.name = name; + this.direction = direction; + this.row = row; + this.col = col; + } + + public static Fish builder(Fish fish) { + return new Fish(fish.name, fish.direction, fish.row, fish.col); + } + +} + +class Shark { + int sum; + int direction; + int row, col; + + public Shark() { + this.sum = 0; + } + + public Shark(int sum, int direction, int row, int col) { + this.sum = sum; + this.direction = direction; + this.row = row; + this.col = col; + } + + public void eat(Fish fish, int row, int col) { + this.sum += fish.name; + this.direction = fish.direction; + this.row = row; + this.col = col; + } +} +``` diff --git a/source/soyeon/[boj] 19237.md b/source/soyeon/[boj] 19237.md new file mode 100644 index 0000000..be7a693 --- /dev/null +++ b/source/soyeon/[boj] 19237.md @@ -0,0 +1,249 @@ +# 백준 문제 + +- 어른 상어 + - https://www.acmicpc.net/problem/19237 + +### 문제 이해하기 +맨 처음에는 모든 상어가 자신의 위치에 자신의 냄새를 뿌린다. + +그 후 1초마다 모든 상어가 동시에 상하좌우로 인접한 칸 중 하나로 이동하고, 자신의 냄새를 그 칸에 뿌린다. 냄새는 상어가 k번 이동하고 나면 사라진다. + +각 상어가 이동 방향을 결정할 때는, 먼저 인접한 칸 중 아무 냄새가 없는 칸의 방향으로 잡는다. 그런 칸이 없으면 자신의 냄새가 있는 칸의 방향으로 잡는다. + +### 문제 접근 방법 + +큐에 상어가 움직일수 있는 위치를 넣어두고 나중에 상어를 한꺼번에 움직인다 + +### 구현 배경 지식 + +시뮬레이션 + +### 접근 방법을 적용한 코드 +```java +import java.util.*; + +public class Main { + public static final int[] dr = { 0, -1, 1, 0, 0 }; + public static final int[] dc = { 0, 0, 0, -1, 1 }; + + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + int size = in.nextInt(); + int sharkNum = in.nextInt(); + int timeLimit = in.nextInt(); + + Square mat[][] = new Square[size][size]; + Shark shark[] = new Shark[sharkNum + 1]; + + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + int num = in.nextInt(); + mat[i][j] = new Square(num, 0); + if (num != 0) { + shark[num] = new Shark(num, i, j); + } + } + } + + for (int i = 1; i <= sharkNum; i++) { + int direction = in.nextInt(); + shark[i].setDirection(direction); + } + + for (int i = 1; i <= sharkNum; i++) { + int directionPrefer[][] = new int[5][5]; + for (int j = 1; j <= 4; j++) { + for (int k = 1; k <= 4; k++) { + directionPrefer[j][k] = in.nextInt(); + } + } + shark[i].setDirectionPrefer(directionPrefer); + } + + int time = simulateShark(shark, mat, timeLimit); + System.out.println(time - 1); + + } + + public static int simulateShark(Shark[] shark, Square[][] mat, int timeLimit) { + int time = 1; + while (time <= 1001) { // time이 1부터 시작인데 1000까지 했어서 틀렸었다... + if (isFinish(shark)) { + return time; + } + + Queue sharkList = new LinkedList(); + for (int i = 1; i < shark.length; i++) { + Shark sharkNow = shark[i]; + if (sharkNow == null) { + continue; + } + int newRow = sharkNow.row; + int newCol = sharkNow.col; + boolean isMoved = false; + for (int j = 1; j <= 4; j++) { + int direction = sharkNow.directionPrefer[sharkNow.direction][j]; + if (isPossibleMoveBlank(mat, newRow + dr[direction], newCol + dc[direction], time, timeLimit)) { + Shark sharkNext = new Shark(sharkNow.name, newRow + dr[direction], newCol + dc[direction]); + sharkNext.direction = direction; + sharkNext.directionPrefer = shark[i].directionPrefer; + sharkList.add(sharkNext); + isMoved = true; + break; + } + } + + if (isMoved) { + continue; + } + + for (int j = 1; j <= 4; j++) { + int direction = sharkNow.directionPrefer[sharkNow.direction][j]; + if (isPossibleMovePrevious(mat, newRow + dr[direction], newCol + dc[direction], i)) { + Shark sharkNext = new Shark(sharkNow.name, newRow + dr[direction], newCol + dc[direction]); + sharkNext.direction = direction; + sharkNext.directionPrefer = shark[i].directionPrefer; + sharkList.add(sharkNext); + isMoved = true; + break; + } + } + } + Shark[] nextShark = new Shark[shark.length]; + while (!sharkList.isEmpty()) { + Shark sharkNow = sharkList.poll(); + if (nextShark[sharkNow.name] != null) { + continue; + } + if (mat[sharkNow.row][sharkNow.col].shark == sharkNow.name + || time - mat[sharkNow.row][sharkNow.col].smellLimit > timeLimit + || mat[sharkNow.row][sharkNow.col].shark == 0) { + nextShark[sharkNow.name] = sharkNow; + } + } + + for (int i = 1; i < nextShark.length; i++) { + Shark sharkNow = nextShark[i]; + if (sharkNow == null) { + continue; + } + if (mat[sharkNow.row][sharkNow.col].smellLimit == time) { + nextShark[i] = null; + continue; + } + mat[sharkNow.row][sharkNow.col] = new Square(sharkNow.name, time); + } + shark = nextShark; + time++; + + } + + return 0; + } + + public static void printSquare(Square[][] mat) { + for (int i = 0; i < mat.length; i++) { + for (int j = 0; j < mat.length; j++) { + System.out.print(mat[i][j]); + } + System.out.println(); + } + System.out.println("--------------------------"); + } + + public static boolean isFinish(Shark shark[]) { + for (int i = 2; i < shark.length; i++) { + if (shark[i] != null) { + return false; + } + } + return true; + } + + public static boolean isPossibleMoveBlank(Square[][] mat, int row, int col, int time, int timeLimit) { + if (!isPossibleBoundary(mat.length, row, col)) { + return false; + } + if (mat[row][col].shark == 0) { + return true; + } + if (time - mat[row][col].smellLimit > timeLimit) { + return true; + } + return false; + } + + public static boolean isPossibleMovePrevious(Square[][] mat, int row, int col, int sharkNum) { + if (!isPossibleBoundary(mat.length, row, col)) { + return false; + } + if (mat[row][col].shark == sharkNum) { + return true; + } + return false; + } + + public static boolean isPossibleBoundary(int size, int row, int col) { + if (row < 0) { + return false; + } + if (col < 0) { + return false; + } + if (row >= size) { + return false; + } + if (col >= size) { + return false; + } + return true; + } +} + +class Shark { + int name; + int direction; + int row; + int col; + int[][] directionPrefer; + + public Shark(int name, int row, int col) { + this.name = name; + this.row = row; + this.col = col; + } + + public void setDirection(int direction) { + this.direction = direction; + } + + public void setDirectionPrefer(int[][] directionPrefer) { + this.directionPrefer = directionPrefer; + } + + @Override + public String toString() { + return name + " (" + row + "," + col + ")"; + } +} + +class Square { + int shark; + int smellLimit; + + public Square(int shark, int smellLimit) { + this.shark = shark; + this.smellLimit = smellLimit; + } + + public Square(Square square) { + this.shark = square.shark; + this.smellLimit = square.smellLimit; + } + + @Override + public String toString() { + return "(" + shark + "," + smellLimit + ")"; + } +} +``` diff --git a/source/soyeon/[boj] 19238.md b/source/soyeon/[boj] 19238.md new file mode 100644 index 0000000..3717e16 --- /dev/null +++ b/source/soyeon/[boj] 19238.md @@ -0,0 +1,256 @@ +# 백준 문제 + +- 스타트 택시 + - https://www.acmicpc.net/problem/19238 + +### 문제 이해하기 +백준이 태울 승객을 고를 때는 현재 위치에서 최단거리가 가장 짧은 승객을 고른다. + +그런 승객이 여러 명이면 그중 행 번호가 가장 작은 승객을, 그런 승객도 여러 명이면 그중 열 번호가 가장 작은 승객을 고른다. + +택시와 승객이 같은 위치에 서 있으면 그 승객까지의 최단거리는 0이다. --> 이거 주의해야 됨 + +연료는 한 칸 이동할 때마다 1만큼 소모된다. + +한 승객을 목적지로 성공적으로 이동시키면, 그 승객을 태워 이동하면서 소모한 연료 양의 두 배가 충전된다. + +이동하는 도중에 연료가 바닥나면 이동에 실패하고, 그 날의 업무가 끝난다 + +### 문제 접근 방법 + +그냥 구현 + +### 구현 배경 지식 + +priorityQueue, 시뮬레이션 + +### 접근 방법을 적용한 코드 +```java +import java.util.*; + +public class Main { + public static final int[] dr = { -1, 0, 0, 1 }; + public static final int[] dc = { 0, -1, 1, 0 }; + + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + int size = in.nextInt(); + int peopleNum = in.nextInt(); + long initFuel = in.nextLong(); + + Taxi taxi = new Taxi(initFuel); + + int mat[][] = new int[size + 1][size + 1]; + for (int i = 1; i <= size; i++) { + for (int j = 1; j <= size; j++) { + mat[i][j] = (in.nextInt()) * -1; + } + } + + int row = in.nextInt(); + int col = in.nextInt(); + taxi.setPosition(row, col); + + Person[] people = new Person[peopleNum + 1]; + for (int i = 1; i <= peopleNum; i++) { + int startRow = in.nextInt(); + int startCol = in.nextInt(); + int endRow = in.nextInt(); + int endCol = in.nextInt(); + + people[i] = new Person(startRow, startCol, endRow, endCol); + mat[startRow][startCol] = i; + } + + while (peopleNum != 0) { + Point point = getClosestPerson(mat, taxi); + + if (point == null) { + taxi.fuel = -1; + break; + } + + taxi.moveTaxiToStartPoint(point); + mat[point.row][point.col] = 0; + if (taxi.fuel < 0) { + taxi.fuel = -1; + break; + } + + Point destPoint = getPathDistance(mat, taxi, people[point.personId]); + if (destPoint == null) { + taxi.fuel = -1; + break; + } + + taxi.moveTaxiToEndPoint(destPoint); + if (taxi.fuel < 0) { + taxi.fuel = -1; + break; + } + peopleNum--; + } + + if (peopleNum > 0) { + System.out.println("-1"); + } else { + System.out.println(taxi.fuel); + } + } + + public static void printMat(int mat[][]) { + for (int i = 1; i < mat.length; i++) { + for (int j = 1; j < mat.length; j++) { + System.out.print(mat[i][j] + " "); + } + System.out.println(); + } + System.out.println("-----------------"); + } + + public static Point getPathDistance(int[][] mat, Taxi taxi, Person person) { + int size = mat.length; + boolean[][] isVisited = new boolean[mat.length][mat.length]; + + PriorityQueue queue = new PriorityQueue(); + queue.add(new Point(taxi.row, taxi.col, 0, 0)); + + while (!queue.isEmpty()) { + Point point = queue.poll(); + if (point.row == person.endRow && point.col == person.endCol) { + return point; + } + + for (int i = 0; i < 4; i++) { + int newRow = point.row + dr[i]; + int newCol = point.col + dc[i]; + + if (isBoundary(newRow, newCol, size) && mat[newRow][newCol] >= 0 && !isVisited[newRow][newCol] + && point.distance + 1 <= taxi.fuel) { + queue.add(new Point(newRow, newCol, point.distance + 1, 0)); + isVisited[newRow][newCol] = true; + } + } + } + return null; + } + + public static Point getClosestPerson(int[][] mat, Taxi taxi) { + int size = mat.length; + boolean[][] isVisited = new boolean[mat.length][mat.length]; + + PriorityQueue queue = new PriorityQueue(); + queue.add(new Point(taxi.row, taxi.col, 0, mat[taxi.row][taxi.col])); + + while (!queue.isEmpty()) { + Point point = queue.poll(); + + if (mat[point.row][point.col] > 0) { + return point; + } + + for (int i = 0; i < 4; i++) { + int newRow = point.row + dr[i]; + int newCol = point.col + dc[i]; + + if (isBoundary(newRow, newCol, size) && mat[newRow][newCol] >= 0 && !isVisited[newRow][newCol] + && point.distance + 1 <= taxi.fuel) { + queue.add(new Point(newRow, newCol, point.distance + 1, mat[newRow][newCol])); + isVisited[newRow][newCol] = true; + } + } + } + return null; + } + + public static boolean isBoundary(int row, int col, int size) { + if (row <= 0) { + return false; + } + if (col <= 0) { + return false; + } + if (row >= size) { + return false; + } + if (col >= size) { + return false; + } + return true; + } +} + +class Taxi { + long fuel; + int row, col; + + public Taxi(long fuel) { + this.fuel = fuel; + } + + public void setPosition(int row, int col) { + this.row = row; + this.col = col; + } + + public void moveTaxiToStartPoint(Point point) { + this.fuel -= (long) point.distance; + this.row = point.row; + this.col = point.col; + } + + public void moveTaxiToEndPoint(Point point) { + this.fuel += (long) point.distance; + this.row = point.row; + this.col = point.col; + } +} + +class Person { + int startRow, startCol; + int endRow, endCol; + int distance; + + public Person(int startRow, int startCol, int endRow, int endCol) { + this.startRow = startRow; + this.startCol = startCol; + this.endRow = endRow; + this.endCol = endCol; + } +} + +class Point implements Comparable { + int row; + int col; + int distance; + int personId; + + public Point(int row, int col, int distance, int personId) { + this.row = row; + this.col = col; + this.distance = distance; + this.personId = personId; + } + + @Override + public int compareTo(Point o) { + if (this.distance < o.distance) { + return -1; + } else if (this.distance == o.distance) { + if (this.row < o.row) { + return -1; + } else if (this.row == o.row) { + if (this.col < o.col) { + return -1; + } else { + return 1; + } + } else { + return 1; + } + } else { + return 1; + } + } +} +``` diff --git a/source/soyeon/[boj] 2110.md b/source/soyeon/[boj] 2110.md new file mode 100644 index 0000000..3c833f7 --- /dev/null +++ b/source/soyeon/[boj] 2110.md @@ -0,0 +1,63 @@ +# 프로그래머스 스택/큐 문제 +- 공유기 설치 + - https://www.acmicpc.net/problem/2110 + +## 풀이 방법 +### 문제 이해하기 +C개의 공유기를 N개의 집에 적당히 설치해서, 가장 인접한 두 공유기 사이의 거리를 최대를 구하는 문제이다. + +### 문제 접근 방법 +공유기를 어떤 거리 D로 설치할 때 모든 집이 와이파이를 사용할수 있는지/없는지 판단하는 문제로 바꾼다. +거리 D를 구할 때에는 이분 탐색으로 구하도록 한다. + +### 구현 배경 지식 +이분 탐색 + +### 접근 방법을 적용한 코드 +```java +import java.util.*; +import java.io.*; + +public class Main { + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + int n = in.nextInt(); + int c = in.nextInt(); + int home[] = new int[n]; + for (int i = 0; i < n; i++) { + home[i] = in.nextInt(); + } + Arrays.sort(home); + + int low = 1; + int high = home[n - 1] - home[0]; + int mid = 0; + while (low < high) { + mid = (low + high) / 2; + + if (isPossible(mid, home, c)) { + low = mid + 1; + } else { + high = mid; + } + } + System.out.println(low); + } + + public static boolean isPossible(int distance, int home[], int c) { + int num = 1; + int currentPos = home[0]; + for (int i = 1; i < home.length; i++) { + if (home[i] - currentPos > distance) { + num += 1; + currentPos = home[i]; + } + } + + if (num < c) { + return false; + } + return true; + } +} +``` diff --git a/source/soyeon/[boj] 2294.md b/source/soyeon/[boj] 2294.md new file mode 100644 index 0000000..6a4ef60 --- /dev/null +++ b/source/soyeon/[boj] 2294.md @@ -0,0 +1,40 @@ +# 백준 문제 + +- 동전2 + - https://www.acmicpc.net/problem/2294 + +### 문제 이해하기 +n가지 종류의 동전이 있다. 이 동전들을 최소 갯수로 사용해서, 그 가치의 합이 k원이 되도록 만드는 문제이다. + +### 문제 접근 방법 + +### 구현 배경 지식 +DP + +### 접근 방법을 적용한 코드 + +```java +import java.util.*; + +public class Main { + public static void main(String[] args){ + Scanner in=new Scanner(System.in); + int n=in.nextInt();//동전 종류 + int k=in.nextInt();//가치의 합 + int coin[]=new int[n]; + for(int i=0;i=0) + dp[i]=Math.min(dp[i], dp[i-coin[j]]+1); + } + } + if(dp[k]>=99999999) System.out.println("-1"); + else System.out.println(dp[k]); + } +} +``` diff --git a/source/soyeon/[boj] 2667.md b/source/soyeon/[boj] 2667.md new file mode 100644 index 0000000..0bf7a44 --- /dev/null +++ b/source/soyeon/[boj] 2667.md @@ -0,0 +1,82 @@ +# 백준 문제 + +- 단지번호붙이기 + - https://www.acmicpc.net/problem/2667 + +### 문제 이해하기 +지도를 입력하여 단지수를 출력하고, 각 단지에 속하는 집의 수를 오름차순으로 정렬하여 출력하는 프로그램이다. + +### 문제 접근 방법 +단지번호를 붙이고 단지에 해당하는 집의 수를 계산한 뒤 array에 넣고 정렬하여 출력하도록 한다. + +### 구현 배경 지식 +DFS + +### 접근 방법을 적용한 코드 +```java +import java.math.*; +import java.util.*; + +public class Main{ + public static int arr[][]; + public static int n; + public static int count; + public static void main(String[] args) { + Scanner in=new Scanner(System.in); + n=in.nextInt(); + arr=new int[n+2][n+2]; + for(int i=1;i<=n;i++){ + String str=in.next(); + for(int j=1;j<=n;j++){ + arr[i][j]=str.charAt(j-1)-48; + } + } + + String ans=""; + for(int i=1;i<=n;i++){ + for(int j=1;j<=n;j++){ + if(arr[i][j]==1){ + count=1; + dfs(j,i); + if(count==1) ans+=count+" "; + else if(count!=0) ans+=count-1+" "; + } + } + } + + StringTokenizer st=new StringTokenizer(ans," "); + int arr[]=new int[st.countTokens()]; + int flag=0; + while(st.hasMoreTokens()){ + arr[flag++]=Integer.valueOf(st.nextToken()); + } + + System.out.println(arr.length); + Arrays.sort(arr); + for(int i=0;i0 && y>=0 && x-1<=n && y<=n && arr[y][x-1]==1){ + arr[y][x-1]=0; + count++; + dfs(x-1,y); + } + if(x+1>0 && y>=0 && x+1<=n && y<=n && arr[y][x+1]==1){ + arr[y][x+1]=0; + count++; + dfs(x+1,y); + } + if(x>0 && y-1>=0 && x<=n && y-1<=n && arr[y-1][x]==1){ + arr[y-1][x]=0; + count++; + dfs(x,y-1); + } + if(x>0 && y+1>=0 && x<=n && y+1<=n && arr[y+1][x]==1){ + arr[y+1][x]=0; + count++; + dfs(x,y+1); + } + } +} +``` diff --git a/source/soyeon/[boj]2022.md b/source/soyeon/[boj]2022.md new file mode 100644 index 0000000..8da02f3 --- /dev/null +++ b/source/soyeon/[boj]2022.md @@ -0,0 +1,49 @@ +# 백준 문제 + +- 사다리 + - https://www.acmicpc.net/problem/2022 + +### 문제 이해하기 +![그림](https://onlinejudgeimages.s3-ap-northeast-1.amazonaws.com/upload/201007/ladd.png) + +### 문제 접근 방법 +닮음으로 식을 찾는다 + +### 구현 배경 지식 +이분 탐색 + +### 접근 방법을 적용한 코드 +```java +import java.util.*; + +public class Main { + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + double x = in.nextDouble(); + double y = in.nextDouble(); + double h = in.nextDouble(); + + double low = 0; + double high = Math.max(x, y); + double mid = (low + high) / (double) 2; + while (low < high) { + mid = (low + high) / (double) 2; + + double hLeft = Math.sqrt(x * x - mid * mid); + double hRight = Math.sqrt(y * y - mid * mid); + + double calH = hLeft * hRight / (hLeft + hRight); + if (Math.abs(h - calH) < 0.001) { + break; + } + + if (calH > h) { + low = mid; + } else { + high = mid; + } + } + System.out.println(mid); + } +} +``` diff --git a/source/soyeon/[programmers] 17681.md b/source/soyeon/[programmers] 17681.md new file mode 100644 index 0000000..c20ca5e --- /dev/null +++ b/source/soyeon/[programmers] 17681.md @@ -0,0 +1,52 @@ +# 프로그래머스 문제 + +- 비밀지도 + - https://programmers.co.kr/learn/courses/30/lessons/17681 + +### 문제 이해하기 + +### 문제 접근 방법 +각 수를 이진수로 바꾼다 + +### 구현 배경 지식 +Integer.toBinaryString 사용 + +### 접근 방법을 적용한 코드 +```java +class Solution { + public String[] solution(int n, int[] arr1, int[] arr2) { + String[] answer = new String[n]; + + for (int i = 0; i < n; i++) { + StringBuilder sb = new StringBuilder(); + String map1 = Integer.toBinaryString(arr1[i]); + String map2 = Integer.toBinaryString(arr2[i]); + int length1 = map1.length(); + int length2 = map2.length(); + + if (map1.length() < n) { + for (int j = 0; j < n - length1; j++) { + map1 = "0" + map1; + } + } + if (map2.length() < n) { + for (int j = 0; j < n - length2; j++) { + map2 = "0" + map2; + } + } + + for (int j = 0; j < n; j++) { + if (map1.charAt(j) == '1' || map2.charAt(j) == '1') { + sb.append('#'); + } else { + sb.append(' '); + } + } + answer[i] = sb.toString(); + + } + + return answer; + } +} +``` diff --git a/source/soyeon/[programmers] 17682.md b/source/soyeon/[programmers] 17682.md new file mode 100644 index 0000000..7ebb607 --- /dev/null +++ b/source/soyeon/[programmers] 17682.md @@ -0,0 +1,74 @@ +# 프로그래머스 문제 + +- 다트 게임 + - https://programmers.co.kr/learn/courses/30/lessons/17682 + +### 문제 이해하기 + +- Single(S), Double(D), Triple(T) 영역이 존재하고 각 영역 당첨 시 점수에서 1제곱, 2제곱, 3제곱 +- 스타상(*): 해당 점수와 바로 전에 얻은 점수를 각 2배로 +- 아차상(#): 해당 점수는 마이너스 + +### 문제 접근 방법 +점수를 스택에 넣으면서 해당 점수와 이전 점수를 계산한다. + +### 구현 배경 지식 +스택 + +### 접근 방법을 적용한 코드 +```java +import java.util.*; +class Solution { + private static final int SINGLE = 1; + private static final int DOUBLE = 2; + private static final int TRIPLE = 3; + + public int solution(String dartResult) { + Stack scores = new Stack(); + StringTokenizer st = new StringTokenizer(dartResult, "SDT*#", true); + String token = st.nextToken(); + scores.add(Integer.valueOf(token)); + + while (st.hasMoreTokens()) { + token = st.nextToken(); + + if (token.equals("S")) { + int score = scores.pop(); + scores.add((int) Math.pow(score, SINGLE)); + } else if (token.equals("D")) { + int score = scores.pop(); + scores.add((int) Math.pow(score, DOUBLE)); + } else if (token.equals("T")) { + int score = scores.pop(); + scores.add((int) Math.pow(score, TRIPLE)); + } else if (token.equals("*")) { + int scoreLast = scores.pop(); + int scoreFirst = 0; + if (!scores.isEmpty()) { + scoreFirst = scores.pop(); + } + + if (scoreFirst != 0) { + scores.add(scoreFirst * 2); + } + scores.add(scoreLast * 2); + } else if (token.equals("#")) { + int score = scores.pop(); + scores.add(score * -1); + } else { + scores.add(Integer.valueOf(token)); + } + } + + return calcScores(scores); + } + + public int calcScores(Stack scores) { + int answer = 0; + while (!scores.isEmpty()) { + answer += scores.pop(); + } + return answer; + } +} +``` diff --git a/source/soyeon/[programmers] 42889.md b/source/soyeon/[programmers] 42889.md new file mode 100644 index 0000000..08ed0e3 --- /dev/null +++ b/source/soyeon/[programmers] 42889.md @@ -0,0 +1,102 @@ +# 프로그래머스 문제 + +- 실패율 + - https://programmers.co.kr/learn/courses/30/lessons/42889 + +### 문제 이해하기 +실패율 = 스테이지에 도달했으나 아직 클리어하지 못한 플레이어의 수 / 스테이지에 도달한 플레이어 수 + +### 문제 접근 방법 + +### 구현 배경 지식 +객체 정렬 + +### 접근 방법을 적용한 코드 +```java +import java.util.*; +class Solution { + public int[] solution(int N, int[] stages) { + int[] answer = new int[N]; + Stage[] stage = new Stage[N + 1]; + for (int i = 0; i <= N; i++) { + stage[i] = new Stage(i); + } + + for (int i = 0; i < stages.length; i++) { + if (stages[i] <= N) { + stage[stages[i]].add(); + } + } + + int num = stages.length; + for (int i = 1; i <= N; i++) { + stage[i].setFailPer(num); + num -= stage[i].getFail(); + } + + Arrays.sort(stage); + + int index = 0; + for (Stage subStage : stage) { + if (subStage.getStageNum() == 0) { + continue; + } + + answer[index++] = subStage.getStageNum(); + } + + return answer; + } +} + +class Stage implements Comparable { + private int fail; + private int stageNum; + private double failPer; + + public Stage(int stageNum) { + this.stageNum = stageNum; + this.fail = 0; + } + + public void add() { + this.fail++; + } + + public void setFailPer(int people) { + if (people == 0) { + this.failPer = 0; + } else { + this.failPer = fail / (double) people; + } + } + + public int getFail() { + return this.fail; + } + + public int getStageNum() { + return this.stageNum; + } + + @Override + public int compareTo(Stage o) { + if (this.failPer > o.failPer) { + return -1; + } else if (this.failPer == o.failPer) { + if (this.stageNum > o.stageNum) { + return 1; + } else { + return -1; + } + } else { + return 1; + } + } + + @Override + public String toString() { + return "( " + stageNum + ": " + fail + " )"; + } +} +``` diff --git a/source/soyeon/[programmers] 42890.md b/source/soyeon/[programmers] 42890.md new file mode 100644 index 0000000..6a2ab09 --- /dev/null +++ b/source/soyeon/[programmers] 42890.md @@ -0,0 +1,193 @@ +# 프로그래머스 문제 + +- 후보키 + - https://programmers.co.kr/learn/courses/30/lessons/42890 + +### 문제 이해하기 +후보 키가 될 수 있는 키 쌍을 구하는 문제이다. + +### 문제 접근 방법 +hashSet에 모든 쌍을 구한 뒤, 해당 키 쌍이 후보 키가 될 수 있는지 각각 확인한다. +후보 키가 될 수 있다면 이미 구한 후보 키 set에 포함되는지 확인한다. + +### 구현 배경 지식 +완전탐색 + +### 접근 방법을 적용한 코드 + +정확성: 67.9 / 100.0 + +```java +import java.util.*; +class Solution { + public static int ans = 0; + public static ArrayList> candidateKeys; + + public int solution(String[][] relation) { + + candidateKeys = new ArrayList>(); + HashSet keySet = new HashSet(); + for (int i = 0; i < relation[0].length; i++) { + keySet.add(i); + createCandidateKey(1, new HashSet(keySet), relation); + keySet.remove(i); + } + + return candidateKeys.size(); + } + + public void createCandidateKey(int index, HashSet keySet, String[][] relation) { + + if (index == relation[0].length) { + // System.out.println(keySet); + if (checkCandidateKey(keySet, relation)) { + for (int i = 0; i < candidateKeys.size(); i++) { + HashSet savedSet=candidateKeys.get(i); + if(savedSet.size()>keySet.size() && containsAll(savedSet, keySet)) { + candidateKeys.remove(savedSet); + candidateKeys.add(keySet); + return; + } + if(savedSet.size()<=keySet.size() && containsAll(keySet, savedSet)) { + return; + } + } + //System.out.println(keySet); + candidateKeys.add(keySet); + } + return; + } + + createCandidateKey(index + 1, new HashSet(keySet), relation); + for (int i = index; i < relation[0].length; i++) { + keySet.add(i); + createCandidateKey(index + 1, new HashSet(keySet), relation); + keySet.remove(i); + } + + } + + public static boolean containsAll(HashSet big, HashSet small) { + Iterator it = small.iterator(); + + boolean isContainsAll=true; + while (it.hasNext()) { + if (!big.contains(it.next())) { + isContainsAll=false; + } + } + return isContainsAll; + } + + public boolean checkCandidateKey(HashSet keySet, String[][] relation) { + HashSet set = new HashSet(); + for (int i = 0; i < relation.length; i++) { + StringBuilder sb = new StringBuilder(""); + Iterator it = keySet.iterator(); + while (it.hasNext()) { + int key = (int) it.next(); + sb.append(relation[i][key] + " "); + } + String newString = sb.toString(); + if (set.contains(newString)) { + return false; + } + + set.add(newString); + } + return true; + } +} +``` + +### 틀린 이유 +맨 첫 요소를 선택하지 않는 경우를 빼먹었다 + +### 문제를 해결한 코드 + +```java +class Solution { + public static int ans = 0; + public static ArrayList> candidateKeys; + + public int solution(String[][] relation) { + + candidateKeys = new ArrayList>(); + HashSet keySet = new HashSet(); + createCandidateKey(1, new HashSet(keySet), relation); // 이 부분 추가 + + for (int i = 0; i < relation[0].length; i++) { + keySet.add(i); + createCandidateKey(1, new HashSet(keySet), relation); + keySet.remove(i); + } + + return candidateKeys.size(); + } + + public void createCandidateKey(int index, HashSet keySet, String[][] relation) { + + if (index == relation[0].length) { + if (checkCandidateKey(keySet, relation)) { + for (int i = 0; i < candidateKeys.size(); i++) { + HashSet savedSet=candidateKeys.get(i); + if(savedSet.size()>keySet.size() && containsAll(savedSet, keySet)) { + // keySet이 더 작은 후보 키인 경우 + candidateKeys.remove(savedSet); + candidateKeys.add(keySet); + return; + } + if(savedSet.size()<=keySet.size() && containsAll(keySet, savedSet)) { + // 중복 또는 이미 더 작은 후보 키를 찾은 경우 + return; + } + } + + // 기존 후보 키와 겹치는 것이 없는 경우 + candidateKeys.add(keySet); + } + return; + } + + createCandidateKey(index + 1, new HashSet(keySet), relation); + for (int i = index; i < relation[0].length; i++) { + keySet.add(i); + createCandidateKey(index + 1, new HashSet(keySet), relation); + keySet.remove(i); + } + + } + + public static boolean containsAll(HashSet big, HashSet small) { + Iterator it = small.iterator(); + + boolean isContainsAll=true; + while (it.hasNext()) { + if (!big.contains(it.next())) { + isContainsAll=false; + } + } + return isContainsAll; + } + + public boolean checkCandidateKey(HashSet keySet, String[][] relation) { + HashSet set = new HashSet(); + for (int i = 0; i < relation.length; i++) { + StringBuilder sb = new StringBuilder(""); + Iterator it = keySet.iterator(); + while (it.hasNext()) { + int key = it.next(); + sb.append(relation[i][key] + " "); + } + String newString = sb.toString(); + if (set.contains(newString)) { + return false; + } + + set.add(newString); + } + return true; + } +} +``` + diff --git a/source/soyeon/[programmers] 42895.md b/source/soyeon/[programmers] 42895.md new file mode 100644 index 0000000..11c00e9 --- /dev/null +++ b/source/soyeon/[programmers] 42895.md @@ -0,0 +1,55 @@ +# 프로그래머스 문제 + +- N으로 표현 + - https://programmers.co.kr/learn/courses/30/lessons/42895 + +### 문제 이해하기 +N과 사칙연산만 사용해서 표현 할 수 있는 방법 중 N 사용횟수의 최솟값을 return 하는 문제이다 + +### 문제 접근 방법 +최솟값이 8보다 크면 -1을 return 조건이 있으므로 모든 경우를 해본다. + +### 구현 배경 지식 +dfs + +### 문제를 해결한 코드 +```java +class Solution { + public static int answer = 9; + + public int solution(int N, int number) { + dfs(0, 0, N, number); + return answer > 8 ? -1 : answer; + } + + public void dfs(int cnt, int number, int N, int target) { + if (cnt > 8) { + return; + } + + if (number == target) { + answer = Math.min(answer, cnt); + return; + } + + for (int i = 1; i <= 8 - cnt; i++) { + dfs(cnt + i, number + getNum(i, N), N, target); + dfs(cnt + i, number - getNum(i, N), N, target); + dfs(cnt + i, number / getNum(i, N), N, target); + dfs(cnt + i, number * getNum(i, N), N, target); + } + } + + public int getNum(int digit, int N) { + int result = 0; + for (int i = 0; i < digit; i++) { + result += Math.pow(10, i) * N; + } + return result; + } +} +``` + +### 문제를 해결한 방법 +* [참고](https://zin0-0.tistory.com/82) + diff --git a/source/soyeon/[programmers] 42898.md b/source/soyeon/[programmers] 42898.md new file mode 100644 index 0000000..d0425ad --- /dev/null +++ b/source/soyeon/[programmers] 42898.md @@ -0,0 +1,71 @@ +# 프로그래머스 + +- 등굣길 + - https://programmers.co.kr/learn/courses/30/lessons/42898 + +### 문제 이해하기 +오른쪽과 아래쪽으로만 움직여 집에서 학교까지 갈 수 있는 최단경로의 개수를 1,000,000,007로 나눈 나머지를 구하는 문제이다. + +### 문제 접근 방법 +위나 왼쪽에서 올 수 있는 경로의 가짓수를 더해준다. + +### 구현 배경 지식 +dp?? + +### 접근 방법을 적용한 코드 +```java +import java.util.*; +class Solution { + public long solution(int m, int n, int[][] puddles) { + long mat[][] = new long[n + 1][m + 1]; + for (int i = 1; i < mat.length; i++) { + Arrays.fill(mat[i], -100); + mat[i][0] = 0; + } + + for (int i = 0; i < puddles.length; i++) { + mat[puddles[i][1]][puddles[i][0]] = -1; + } + + mat[1][1] = 1; + + findAll(n, m, mat); + //printMat(mat); + + return mat[n][m]; + } + + public long findAll(int row, int col, long[][] mat) { + if (mat[row][col] != -100) { + return mat[row][col]; + } + long left = findAll(row, col - 1, mat); + long upper = findAll(row - 1, col, mat); + + if (left == -1 && upper == -1) { + mat[row][col] = 0; + return mat[row][col]; + } + if (left == -1) { + mat[row][col] = upper; + return mat[row][col]; + } + if (upper == -1) { + mat[row][col] = left; + return mat[row][col]; + } + + mat[row][col] = (left + upper) % 1000000007; + return mat[row][col]; + } + + public static void printMat(long[][] mat) { + for (int i = 0; i < mat.length; i++) { + for (int j = 0; j < mat[i].length; j++) { + System.out.print(mat[i][j] + " "); + } + System.out.println(); + } + } +} +``` diff --git a/source/soyeon/[programmers] 43105.md b/source/soyeon/[programmers] 43105.md new file mode 100644 index 0000000..8c1589a --- /dev/null +++ b/source/soyeon/[programmers] 43105.md @@ -0,0 +1,42 @@ +# 프로그래머스 문제 + +- 정수 삼각형 + - https://programmers.co.kr/learn/courses/30/lessons/43105 + +### 문제 이해하기 +삼각형의 꼭대기에서 바닥까지 이어지는 경로 중, 거쳐간 숫자의 합이 가장 큰 경우를 찾는 문제 + +### 문제 접근 방법 +위에서부터 내려오면서 큰 것을 저장하도록 한다. + +### 구현 배경 지식 +dp + +### 접근 방법을 적용한 코드 +```java +import java.util.*; +class Solution { + public int solution(int[][] triangle) { + int row = triangle.length; + + for (int i = 1; i < row; i++) { + int col = triangle[i].length; + for (int j = 0; j < col; j++) { + if (j == 0) { + triangle[i][j] += triangle[i - 1][j]; + } else if (j == col - 1) { + triangle[i][j] += triangle[i - 1][j - 1]; + } else { + triangle[i][j] += Math.max(triangle[i - 1][j - 1], triangle[i - 1][j]); + } + } + } + + int max = 0; + for (int i = 0; i < triangle[row - 1].length; i++) { + max = Math.max(max, triangle[row - 1][i]); + } + return max; + } +} +``` diff --git a/source/soyeon/[programmers] 43163.md b/source/soyeon/[programmers] 43163.md new file mode 100644 index 0000000..3044993 --- /dev/null +++ b/source/soyeon/[programmers] 43163.md @@ -0,0 +1,60 @@ +# 프로그래머스 문제 + +- 단어 변환 + - https://programmers.co.kr/learn/courses/30/lessons/43163 + +### 문제 이해하기 +두 개의 단어 begin, target과 단어의 집합 words가 매개변수로 주어질 때, 최소 몇 단계의 과정을 거쳐 begin을 target으로 변환할 수 있는지 찾는 문제 + +### 문제 접근 방법 +index에 해당하는 char을 바꿔주면서 단어 변환을 모든 경우의 수를 한 담에 최소 경우를 찾는다. + +### 구현 배경 지식 +dfs + +### 접근 방법을 적용한 코드 +```java +class Solution { + public boolean isDiffOneChar(String str1, String str2) { + int cnt = 0; + for (int i = 0; i < str1.length() && cnt < 2; i++) { + if (str1.charAt(i) != str2.charAt(i)) { + cnt++; + } + } + return cnt == 1; + } + + public int dfs(String begin, String target, int index, String[] words, boolean[] visited, int cnt) { + if (begin.equals(target)) { + return cnt; + } + if (visited[index]) { + return cnt; + } + visited[index] = true; + int ans = 0; + for (int i = 0; i < words.length; i++) { + if (index != i && isDiffOneChar(begin, words[i]) && !visited[i]) { + ans = dfs(words[i], target, i, words, visited, cnt + 1); + } + } + return ans; + } + + public int solution(String begin, String target, String[] words) { + int answer = words.length + 1; + for (int i = 0; i < words.length; i++) { + boolean[] visited = new boolean[words.length]; + if (isDiffOneChar(begin, words[i])) { + answer = Math.min(answer, dfs(words[i], target, i, words, visited, 1)); + } + } + if (answer == words.length + 1) { + return 0; + } + return answer; + } + +} +``` diff --git a/source/soyeon/[programmers] 43238.md b/source/soyeon/[programmers] 43238.md new file mode 100644 index 0000000..0c03fd6 --- /dev/null +++ b/source/soyeon/[programmers] 43238.md @@ -0,0 +1,57 @@ +# 프로그래머스 문제 + +- 입국심사 + - https://programmers.co.kr/learn/courses/30/lessons/43238 + +### 문제 이해하기 +* 입국심사를 기다리는 사람 수 n +* 각 심사관이 한 명을 심사하는데 걸리는 시간이 담긴 배열 times +* 모든 사람이 심사를 받는데 걸리는 시간의 최솟값을 return 하는 문제 +* 주의: 최솟값을 구하는 문제이므로 심사대가 비지만 그곳에서 심사를 받지 않고 더 기다린 후에 다른 심사대에서 심사를 받는 경우가 있을 수 있다. + +### 문제 접근 방법 +* 어떤 시간이 주어졌을 때, 심사관들이 몇 명을 심사할 수 있는지 계산하여 n보다 크면 시간을 줄이고, n보다 작으면 시간을 늘린다 + +### 구현 배경 지식 +이분탐색 + +### 문제를 해결한 코드 +```java +import java.util.*; +class Solution { + public long solution(int n, int[] times) { + Arrays.sort(times); + + long min=1; + long max=(long)times[times.length-1]*n; + long mid=0; + long sum; + long answer = max; + + while(min<=max){ + sum=0; + mid=(min+max)/2; + + for(int time:times){ + sum+=mid/time; + } + + if(sum>=n){ + if(mid stack = new Stack(); + + stack.push(str.charAt(0)); + + for (int i = 1; i < str.length(); i++) { + if (str.charAt(i) == '(') { + stack.push(str.charAt(i)); + } else { + if (stack.isEmpty()) { + return false; + } + + stack.pop(); + } + } + + if (!stack.isEmpty()) { + return false; + } + + return true; + } +} +``` diff --git a/source/soyeon/[programmers] 60060.md b/source/soyeon/[programmers] 60060.md new file mode 100644 index 0000000..3251607 --- /dev/null +++ b/source/soyeon/[programmers] 60060.md @@ -0,0 +1,214 @@ +# 프로그래머스 문제 +- 가사 검색 + - https://www.welcomekakao.com/learn/courses/30/lessons/60060?language=java + +## 풀이 방법 +### 문제 이해하기 +각 query 가 매치되는 word의 수를 배열 형태로 리턴하는 문제이다. + +### 문제 접근 방법 +ArrayList의 배열 + +### 구현 배경 지식 + +### 접근 방법을 적용한 코드 +```java +/* 정확성: 25.0 + * 효율성: 30.0 + * 합계: 55.0 / 100.0 */ +import java.util.*; +class Solution { + public static char WILD_CARD = '?'; + + public int[] solution(String[] words, String[] queries) { + + int queryLength = queries.length; + int[] answer = new int[queryLength]; + + ArrayList wordLists[] = new ArrayList[10001]; + for (int i = 0; i < words.length; i++) { + int wordLength = words[i].length(); + if (wordLists[wordLength] == null) { + wordLists[wordLength] = new ArrayList(); + } + wordLists[wordLength].add(words[i]); + } + + for (int i = 0; i < queryLength; i++) { + answer[i] = matchedQuery(queries[i], wordLists); + } + + return answer; + } + + public int matchedQuery(String query, ArrayList wordLists[]) { + int answer = 0; + ArrayList sameLengthWords = wordLists[query.length()]; + + if (sameLengthWords == null) { + return 0; + } + + for (String word : sameLengthWords) { + if (isMatched(query, word)) { + answer++; + } + } + return answer; + } + + public boolean isMatched(String query, String word) { + if (query.length() != word.length()) { + return false; + } + + for (int i = 0; i < query.length(); i++) { + if (query.charAt(i) != word.charAt(i)) { + if (query.charAt(i) != WILD_CARD && word.charAt(i) != WILD_CARD) { + return false; + } + } + } + return true; + } +} +``` + +### 해결하지 못한 이유 +시간 초과 + +### 문제를 해결한 코드 +```java +import java.util.*; + +public class Main { + public static void main(String[] args) { + String words[] = { "frodo", "front", "frost", "frozen", "frame", "kakao" }; + String queries[] = { "fro??", "????o", "fr???", "fro???", "pro?" }; + Solution solution = new Solution(); + int[] answer = solution.solution(words, queries); + for (int i = 0; i < answer.length; i++) { + System.out.print(answer[i] + " "); + } + } +} + +class Solution { + + public int[] solution(String[] words, String[] queries) { + + Trie[] tries = new Trie[100001]; + for (String word : words) { + int wordLength = word.length(); + if (tries[wordLength] == null) { + tries[wordLength] = new Trie(); + } + + tries[wordLength].insert(word); + } + + int[] answer = new int[queries.length]; + for (int i = 0; i < queries.length; i++) { + int len = queries[i].length(); + if (tries[len] == null) { + answer[i] = 0; + } else { + answer[i] = tries[len].getCount(queries[i]); + } + } + return answer; + } +} + +class Trie { + public static char WILD_CARD = '?'; + + private Node frontRootNode; + private Node backRootNode; + + Trie() { + frontRootNode = new Node(); + backRootNode = new Node(); + } + + public void insert(String word) { + insertFront(word); + insertBack(word); + } + + private void insertFront(String word) { + Node node = frontRootNode; + for (int i = 0; i < word.length(); i++) { + node.count++; + // word.charAt(i) 가 children에 없는 경우 새로운 Node를 만든다. + node = node.children.computeIfAbsent(word.charAt(i), c -> new Node()); + } + } + + private void insertBack(String word) { + Node node = backRootNode; + for (int i = word.length() - 1; i >= 0; i--) { + node.count++; + node = node.children.computeIfAbsent(word.charAt(i), c -> new Node()); + } + } + + public int getCount(String query) { + if (query.charAt(0) == WILD_CARD) { + return getCountFromBack(query); + } + + return getCountFromFront(query); + } + + private int getCountFromFront(String query) { + Node node = frontRootNode; + + for (int i = 0; i < query.length(); i++) { + if (query.charAt(i) == WILD_CARD) { + break; + } + if (!node.children.containsKey(query.charAt(i))) { + return 0; + } + node = node.children.get(query.charAt(i)); + } + return node.count; + } + + private int getCountFromBack(String query) { + Node node = backRootNode; + + for (int i = query.length() - 1; i >= 0; i--) { + if (query.charAt(i) == WILD_CARD) { + break; + } + if (!node.children.containsKey(query.charAt(i))) { + return 0; + } + node = node.children.get(query.charAt(i)); + } + return node.count; + } + +} + +class Node { + Map children; + int count; + + Node() { + this.children = new HashMap<>(); + this.count = 0; + } +} +``` + +### 문제를 해결한 방법 +trie + +--- +아래의 사이트를 참고해 작성된 글입니다. + +* https://leveloper.tistory.com/99 +* https://woovictory.github.io/2020/04/22/Java-Trie/ diff --git a/source/soyeon/[programmers] 64062.md b/source/soyeon/[programmers] 64062.md new file mode 100644 index 0000000..53f031f --- /dev/null +++ b/source/soyeon/[programmers] 64062.md @@ -0,0 +1,70 @@ +# 프로그래머스 문제 +- 징검다리 건너기 + - https://programmers.co.kr/learn/courses/30/lessons/64062 + +## 풀이 방법 +### 문제 이해하기 +디딤돌에 적힌 숫자가 순서대로 담긴 배열 stones와 한 번에 건너뛸 수 있는 디딤돌의 최대 칸수 k가 매개변수로 주어질 때, +최대 몇 명까지 징검다리를 건널 수 있는지 구하는 문제이다. + +### 문제 접근 방법 +처음에는 단순하게 한 번씩 건너게 해서 몇 명의 친구들이 건너갈 수 있는지 찾아봤는데 효율성 테스트에서 통과하지 못했다. +찾아보니 이분 탐색으로 찾으면 된다고 해서 이분탐색으로 바꾸어 보았다. + +### 구현 배경 지식 +이분탐색 + +### 접근 방법을 적용한 코드 +```java +import java.util.*; + +public class Main { + + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + Solution solution = new Solution(); + int[] stones = { 2, 4, 5, 3, 2, 1, 4, 2, 5, 1 }; + int k = 3; + System.out.println(solution.solution(stones, k)); + } +} + +class Solution { + public static int MAX = 200000000; + + public int solution(int[] stones, int k) { + int left = 1; + int right = MAX; + int mid = (left + right) / 2; + + while (left <= right) { + if (isKSequence(mid, stones, k)) { + left = mid + 1; + } else { + right = mid - 1; + } + + mid = (left + right) / 2; + } + + return left; + } + + public boolean isKSequence(int num, int[] stones, int k) { + int n = 0; + for (int i = 0; i < stones.length; i++) { + if (stones[i] - num <= 0) { + n++; + } else { + n = 0; + } + + if (n >= k) { + // 돌의 높이 == 친구들의 인원인 경우 연속해서 k개 + return false; + } + } + return true; + } +} +``` diff --git a/source/soyeon/[programmers] 64065.md b/source/soyeon/[programmers] 64065.md new file mode 100644 index 0000000..30eee2b --- /dev/null +++ b/source/soyeon/[programmers] 64065.md @@ -0,0 +1,50 @@ +# 프로그래머스 스택/큐 문제 +- 튜플 + - https://programmers.co.kr/learn/courses/30/lessons/64065 + +## 풀이 방법 +### 문제 이해하기 +{{a1}, {a1, a2}, {a1, a2, a3}, {a1, a2, a3, a4}, ... {a1, a2, a3, a4, ..., an}} 이런식으로 튜플을 표현할 수 있다. + +### 문제 접근 방법 +원소가 중복되지 않으므로 a1의 수가 가장 많이 포함 되었고 an이 가장 적게 포함되었다. +이를 이용해서 (원소, 원소의 갯수)로 해시맵에 담아서 원소의 갯수 순으로 내림차순 정렬을 한 순서로 키를 리턴하면 될 것이다. + +### 구현 배경 지식 +HashMap value 기준 정렬 + +### 접근 방법을 적용한 코드 +```java +import java.util.*; +class Solution { + public int[] solution(String s) { + HashMap hashmap = new HashMap(); + + StringTokenizer st = new StringTokenizer(s, "{},", false); + Integer input = 0; + while (st.hasMoreTokens()) { + input = Integer.valueOf(st.nextToken()); + + hashmap.put(input, hashmap.getOrDefault(input, 0) + 1); + } + + // hashmap 의 value 기준 정렬 + List keySetList = new ArrayList<>(hashmap.keySet()); + Collections.sort(keySetList, new Comparator() { + + @Override + public int compare(Integer o1, Integer o2) { + return hashmap.get(o2).compareTo(hashmap.get(o1)); + } + + }); + + int index = 0; + int answer[] = new int[keySetList.size()]; + for (Integer key : keySetList) { + answer[index++] = key; + } + return answer; + } +} +``` diff --git a/source/soyeon/[programmers]42579.md b/source/soyeon/[programmers]42579.md new file mode 100644 index 0000000..d067fbd --- /dev/null +++ b/source/soyeon/[programmers]42579.md @@ -0,0 +1,101 @@ +# 프로그래머스 문제 + +- 베스트앨범 + - https://programmers.co.kr/learn/courses/30/lessons/42579 + +### 문제 이해하기 +- 속한 노래가 많이 재생된 장르를 먼저 수록합니다. +- 장르 내에서 많이 재생된 노래를 먼저 수록합니다. +- 장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래를 먼저 수록합니다. + +### 문제 접근 방법 +해시 맵 정렬 코드를 구현하여 문제를 푼다. + +### 구현 배경 지식 +Comparable, Comparator + +### 접근 방법을 적용한 코드 + +```java +import java.util.*; +class Solution { + public int[] solution(String[] genres, int[] plays) { + ArrayList list=new ArrayList(); + + HashMap add=new HashMap(); + HashMap> map=new HashMap>(); + for(int i=0;i array=map.get(genres[i]); + array.add(new Song(plays[i], i)); + map.put(genres[i], array); + } + else { + add.put(genres[i], plays[i]); + ArrayList array=new ArrayList(); + array.add(new Song(plays[i], i)); + map.put(genres[i], array); + } + } + + Map sortAdd = sortByValue(add); + for (Map.Entry en : sortAdd.entrySet()) { + String genre=en.getKey(); + ArrayList songs=map.get(genre); + songs.sort(new SongComparator()); + for(int i=0;i<=1;i++) { + if(i>=songs.size()) break; + else list.add(songs.get(i).index); + } + } + + + return list.stream().mapToInt(i->i).toArray(); + } + public static HashMap sortByValue(HashMap hm) + { + // Create a list from elements of HashMap + List > list = new LinkedList>(hm.entrySet()); + + // Sort the list 내림차순 + Collections.sort(list, new Comparator >() { + public int compare(Map.Entry o1, Map.Entry o2) { + return (o2.getValue()).compareTo(o1.getValue()); + } + }); + + // put data from sorted list to hashmap + HashMap temp = new LinkedHashMap(); + for (Map.Entry aa : list) { + temp.put(aa.getKey(), aa.getValue()); + } + return temp; + } +} +class Song implements Comparable{ + int play; + int index; + public Song(int play, int index) { + this.play=play; + this.index=index; + } + @Override + public int compareTo(Song o) { + if (this.play < o.play) { + return 1; + } + else if (this.play > o.play) { + return -1; + } + return 0; + } +} +class SongComparator implements Comparator{ + @Override + public int compare(Song arg0,Song arg1) { + return arg0.compareTo(arg1); + } +} +``` + diff --git a/source/woodi/200819_BestAlbum.md b/source/woodi/200819_BestAlbum.md new file mode 100644 index 0000000..fb15cbd --- /dev/null +++ b/source/woodi/200819_BestAlbum.md @@ -0,0 +1,98 @@ +# 프로그래머스 문제 +========================= +- 해시 + +- 프린터 + - https://programmers.co.kr/learn/courses/30/lessons/42579 + +### 풀이 방법 + +#### 문제 접근 방법 +- 노래 수록 기준 1. 속한 노래가 많이 재생된 장르, 2. 장르 내에서 많이 재생된 노래, 3. 장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래 + +- 3가지 기준에 대해서 정렬하기 위해 Music 클래스를 만듦 +- list에 Music들을 담은 뒤, + - 1번 기준 : HashMap에 장르별 재생횟수를 저장 (key : 장르명, value : 재생횟수) + - 1,2,3번 기준에 따라 정렬 +- 장르당 2곡씩 선별해서 최종 추천 리스트 생성 + +### 구현 배경 지식 +- 해시, 정렬 + +#### 접근 방법을 적용한 코드 + +```java + + +class Solution { + public static class Music{ + String genre; + int musicNum; + int play; + public Music(String gen, int mn, int play) { + this.genre= gen; + this.musicNum =mn; + this.play = play; + } + } + public int solution(String[] genres, int[] plays) { + int answer = 0; + List musics = new ArrayList<>(); + for(int i=0; i hashmap에 장르별 재생횟수를 저장 -> key : 장르명, value : 재생횟수 + HashMap hm = new HashMap<>(); + + for(int i=0; i() { + + @Override + public int compare(Music o1, Music o2) { // 항상 첫번째 인자가 두번째 인자보다 작다고 생각해라 + if(!o1.genre.equals(o2.genre)) { + return hm.get(o2.genre).compareTo(hm.get(o1.genre)); // 내림차순 정렬 (큰게 앞에오게) + }else { + if(o1.play!= o2.play) { + return o2.play-o1.play; // 내림차순 정렬 + }else { + return o1.musicNum-o2.musicNum; // 오름차순 정렬 + } + } + } + }); + + // 장르당 2곡씩 count + HashMap cnt = new HashMap<>(); + + // 최종 추천 리스트(answer배열의 크기를 정해주기 위해 따로 담는다) + List finali = new ArrayList<>(); + for(Music m : musics) { + if(!cnt.containsKey(m.genre)) { + cnt.put(m.genre, 1); + finali.add(m); + }else { + if(cnt.get(m.genre)<2) { + cnt.put(m.genre, 2); + finali.add(m); + } + } + } + int[] answer = new int[finali.size()]; + int k=0; + for(Music m : finali) { + answer[k++] = m.musicNum; + } + + return answer; + } +} +``` diff --git a/source/woodi/200821_Ladder.md b/source/woodi/200821_Ladder.md new file mode 100644 index 0000000..815c783 --- /dev/null +++ b/source/woodi/200821_Ladder.md @@ -0,0 +1,66 @@ +# 백준 문제 +- 사다리 + - https://www.acmicpc.net/problem/2022 + +### 풀이 방법 + +#### 문제 접근 방법 +- c = h1*h2 / (h1+h2)라는 방정식에 임의의 d값을 대입하여 주어진 c값과 얼마나 유사한지 검사한다 + +### 구현 배경 지식 +- 이분 탐색 +#### 접근 방법을 적용한 코드 + +```java + +import java.util.*; +class Solution { + + static double getC(double x, double y, double d) { + double h1 = Math.sqrt(x*x-d*d); + double h2 = Math.sqrt(y*y-d*d); + + return h1*h2/(h1+h2); + + } + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + double x = sc.nextDouble(); + double y = sc.nextDouble(); + double c = sc.nextDouble(); + double d; // 우리가 구해야할 값 + + // 입력값 중 더 큰 값을 x에 저장하겠다. + if(y>x) { + double temp = x; + x=y; + y=temp; + } + + double l=0; // d의 최소값 + double r=y; // d의 최대값 + double newC; + + // 이분탐색을 통해 주어진 C와 가장 근사한 newC륾 만드는 d를 찾는다. + while(true) { + d = (l+r)/2; // d의 값을 l과 r의 중간 값으로 잡는다. + newC = getC(x,y,d); + + // c-newC가 0.001미만이면 종료한다. + if(Math.abs(c-newC)<0.001) { + System.out.println(d); + break; + } + + // 폭이 실제보다 좁다. --> d 증가 시켜야 + if(newC >c) { + l=d; + }else { + r=d; + } + + } + + } +} +``` diff --git a/source/woodi/200830_kakaoBlindTest02.md b/source/woodi/200830_kakaoBlindTest02.md new file mode 100644 index 0000000..00097cb --- /dev/null +++ b/source/woodi/200830_kakaoBlindTest02.md @@ -0,0 +1,165 @@ +# 카카오 블라인드 테스트 문제 : 기둥과 보 설치 +========================= +- https://www.welcomekakao.com/learn/courses/30/lessons/60061 + +### 풀이 방법 + +#### 문제 접근 방법 +- 모든 좌표에 Coordinate객체 생성 +- 해당 좌표(x,y)를 꼭지점으로 하는 모든 구조물 저장 + - 설치 시 : isPossible(x좌표, y좌표, 구조물 종류) + - 삭제 시 : (x,y)을 끝점으로 하는 모든 구조물에 대하여 isPossible검사 + + +### 구현 배경 지식 + +#### 접근 방법을 적용한 코드 + +```java + + +import java.util.*; + +class Solution { + static Coordinate[][] check; + static class Coordinate{ + int x; + int y; + String a; + List starts ; + List ends; + public Coordinate(int x, int y) { + this.x = x; + this.y = y; + this.starts = new LinkedList<>(); + this.ends = new LinkedList<>(); + } + // x, y를 시작점으로 하는 구조물 반환 + public List findByStart(int x, int y) { + return this.starts; + } + // x, y를 끝점으로 하는 구조물 반환 + public List findByEnd(int x, int y) { + return this.ends; + } + public void setCoor(int x, int y,String a) { + this.x = x; + this.y = y; + this.a = a; + this.starts.add(a); + if(a.equals("0")) { + check[x][y+1].ends.add(a); + }else if(a.equals("1")) { + check[x+1][y].ends.add(a); + } + } + + public void delete(int x, int y, String a) { + this.starts.remove(a); + this.a ="-1"; + if(a.equals("0") && !check[x][y+1].ends.isEmpty()) { + check[x][y+1].findByEnd(x,y+1).remove(a); + }else if(a.equals("1") && !check[x+1][y].ends.isEmpty()) { + check[x+1][y].findByEnd(x+1,y).remove(a); + } + + } + } + + public static boolean isPossible(int x, int y, String a) { + // 설치 + /* 기둥 : 바닥 위, 보 한쪽 끝 위, 다른 기둥 위 + * 보 : 한쪽 끝 기둥 위, 양쪽 끝 보 + * */ + if(a.equals("0")) {// 기둥 + if(y==0) { + return true; + }else if(!check[x][y].findByStart(x,y).isEmpty() || !check[x][y].findByEnd(x,y).isEmpty()) { + return true; + } + }else if(a.equals("1")){ //보 + if(check[x][y].findByEnd(x,y).contains("0") ||check[x+1][y].findByEnd(x+1,y).contains("0")) { + return true; + } + if(check[x][y].findByEnd(x,y).contains("1") && check[x+1][y].findByStart(x+1, y).contains("1")) { + return true; + } + } + return false; + } + public int[][] solution(int n, int[][] build_frame) { + check = new Coordinate[n+1][n+1]; + for(int i=0; i answerli = new ArrayList<>(); + for(int i=0; i # + - 둘 다 0 -> 공백 +- 비교 결과를 answer[i]에 append한다. + +### 접근 방법1 을 적용한 코드 +```java +class Solution { + public String makingBinaryNum(int num, int n) { + char[] bn = new char[n]; // 0*n 으로 초기화 + for(int i=0; i=1) { + --n; + int q = num/2; + int r = num%2; + bn[n] = (char) (r+'0'); + num = q; + } + String binaryNum = String.valueOf(bn); + return binaryNum; + } + public String[] solution(int n, int[] arr1, int[] arr2) { + String[] answer = new String[arr1.length]; + for(int i=0; i (String[] round_sc) +- calculate(String[] round_sc) 함수에서 규칙을 적용하여 각 회차별 점수를 계산한다. + +### 접근 방법을 적용한 코드 +```java +class Solution { + int cvtbonus(char b) { + if(b=='S') { + return 1; + } else if(b=='D') { + return 2; + } else if(b=='T') { + return 3; + }else { + return 0; + } + } + int[] calculate(String[] round_sc) { + int[] each_round_score = new int[3]; + for(int i=0; i<3; i++) { + int score=0; + int bonus = 0; + for(int idx =0 ; idx=0 && round_sc[i].charAt(idx)-'0'<=10 ) { + if(idx>0 && round_sc[i].charAt(idx-1)-'0'>=0 && round_sc[i].charAt(idx-1)-'0'<=10) { + // 현재 인덱스 숫자, 바로 이전 인덱스도 숫자 -> 숫자가 10 + score =10; + } + else { + score = round_sc[i].charAt(idx)-'0'; + } + }else { // 현재 인덱스 문자 + if(cvtbonus(round_sc[i].charAt(idx))!=0) { + bonus = cvtbonus(round_sc[i].charAt(idx)); + each_round_score[i]+= Math.pow(score, bonus); + }else { + if(round_sc[i].charAt(idx)=='*') { + if(i!=0) { + each_round_score[i-1] = each_round_score[i-1]*2; + } + each_round_score[i] = each_round_score[i]*2; + }else if(round_sc[i].charAt(idx)=='#') { + each_round_score[i] = each_round_score[i]*(-1); + } + } + } + } + } + return each_round_score; + } + public int solution(String dartResult) { + int answer = 0; + String[] round_sc = new String[3]; // round는 3회 + int round =0; + + // 입력 -> 배열에 잘 나눠서 담기 + for(int i=0; i<3; i++) { + round_sc[i]=""; + } + for(int i=0; i0 && !(dartResult.charAt(i-1)-'0'>=0 && dartResult.charAt(i-1)-'0'<=10) + && (dartResult.charAt(i)-'0'>=0 && dartResult.charAt(i)-'0'<=10)) { + round++; + } + round_sc[round]+=dartResult.charAt(i); + + } + // 배열 각 원소 계산하기 + int [] ra = calculate(round_sc); + for(int a : ra) { + answer+=a; + } + return answer; + } +} +``` + diff --git a/source/woodi/200903_kakaoBlindTest05.md b/source/woodi/200903_kakaoBlindTest05.md new file mode 100644 index 0000000..50f8635 --- /dev/null +++ b/source/woodi/200903_kakaoBlindTest05.md @@ -0,0 +1,69 @@ +# 프로그래머스 카카오 블라인드 2019 문제 +- 실패율 + - https://programmers.co.kr/learn/courses/30/lessons/42889 + +## 풀이 방법 +### 문제 이해하기 +- [입력값] + - N : 스테이지 개수 + - int [] stages -> stages[k] : k번째 사용자의 현재 stage 위치 +- [출력값] + - 실패율이 높은 스테이지부터 내림차순으로 스테이지의 번호가 담겨있는 배열을 return +### 문제 접근 방법 +- Stage 클래스 (스테이지 번호, 실패율) implements Comparagle +- 이중 for문 + - 첫번째 for문은 스테이지 번호 (k) + - 두번째 for 문은 사용자 번호 (i)라고 할때, + - stages[i] >= k : 스테이지에 도달한 플레이어 수 + - stages[i] == k : 스테이지에 도달했지만 클리어 못한 플레이어 수 + - 두번째 for문 다 돌때마다 우선순위 큐에 Stage 저장 + +### 접근 방법을 적용한 코드 +```java +class Stage implements Comparable{ + int stage_num; + double fail_rate; + public Stage(int stage_num){ + this.stage_num = stage_num; + this.fail_rate=0; + } + public int compareTo(Stage o) { + if(this.fail_rate == o.fail_rate){ + return Integer.compare(stage_num, o.stage_num); + } + return Double.compare(o.fail_rate, fail_rate); + } +} +class Solution { + public int solution(String dartResult) { + int[] answer = new int[N]; + PriorityQueue q = new PriorityQueue<>(); + for(int k=1; k=k) { + arrived_players++; + if(stages[i]==k) { + not_clear++; + } + } + } + if(arrived_players==0) { + s.fail_rate=0; + }else { + s.fail_rate = (double)not_clear/arrived_players; + } + q.add(s); + } + for(int i=0; i 스택에 a[i]를 추가 + - 다르다 -> (stack.size ==1) s= s+stack.pop() + -> (stack.size > 1) s= s+stack.size() + stack.pop() + -> stack을 완전히 비우고, 스택에 a[i]를 추가한다. +- a의 모든 원소에 대해 검사한 후, 여전히 스택에 남아있는 문자열에 대하여 s에 더해준다. + + + +### 접근 방법을 적용한 코드 +```java +class Solution { + public int splitlen(int n, String s) { + String[] a = {}; + int len = s.length(); + if(len%n !=0) { + a = new String[len/n+1]; + }else { + a = new String[len/n]; + } + int k=0; + int j=0; + while(k s.length()) { + a[j] = s.substring(k, s.length()); + break; + } + a[j] = s.substring(k, temp); + k = temp; + j++; + } + + String ns = ""; // stringBuilder가 좀더 낫다. + Stack st = new Stack<>(); + st.add(a[0]); + for(int i=1; i k ? k : min; + } + answer = min; + return answer; + } +} +``` + diff --git a/source/woodi/200910_kakaoBlindTest07.md b/source/woodi/200910_kakaoBlindTest07.md new file mode 100644 index 0000000..9cdc399 --- /dev/null +++ b/source/woodi/200910_kakaoBlindTest07.md @@ -0,0 +1,98 @@ +# 프로그래머스 카카오 블라인드 2020 문제 +- 괄호변환 + - https://programmers.co.kr/learn/courses/30/lessons/60058 + +## 풀이 방법 +### 문제 이해하기 +- 1) String p가 올바르거나 빈 문자열일 경우 -> 그대로 반환 +- 2) 올바르지 않을 경우 + - p를 u(균형잡힌, 더이상 분리 불가능한) , v로 분리 + - 2-1) u가 올바를 경우 + - return u+solution(v) + - 2-2) u가 올바르지 않을 경우 + - return "(" + solution(v)+")"+ [u의 앞뒤제거하고 괄호 방향 바꾼 결과] +- [입력값] + - Stirng p +- [출력값] + - 괄호 올바르게 변환 +### 문제 접근 방법 +- 주어진 문자열 p는 이미 균형 잡힌 상태 +- boolean isRight(String w) : 문자열 w가 올바른지 판단하는 함수 +- int SplitUV(Stirng w) : 문자열 w를 u,v로 분리했을때, 분리되는 지점의 인덱스 반환 +- solution(String p) : 재귀함수 + + +### 접근 방법을 적용한 코드 +```java +class Solution { + public boolean isRight(String w) { + boolean istrue = true; + Stack s = new Stack<>(); + for(int i=0; i 문자열 반환 + if(p.equals("")) { + return ""; + }else if(isRight(p)) { + return p; + } + // recur : 올바르지 않다 -> u,v로 분할 + // u : 균형 잡힌 + 더이상 분리 불가 + int idx = splitUV(p); + String u = p.substring(0,idx+1); + String v = p.substring(idx+1); + // u가 올바르다 -> u+solution(v) + if(isRight(u)) { + answer = u+solution(v); + }else {// u가 올바르지 않다 -> '('+solution(v)+')' + u의 첫번째 마지막 제거 후 괄호 방향 뒤집어서 뒤에 붙이기 + String nu = u.substring(1,u.length()-1);// 첫번째 마지막 제거 + StringBuilder new_u = new StringBuilder(nu); + for(int i=0; i fish_li = new ArrayList<>(); + Fish[][] map = new Fish[4][4]; + + public Status(int sx, int sy, int sd, Fish[][] copyMapFrom, int score ) { + this.shark_x = sx; + this.shark_y = sy; + this.shark_direct = sd; + this.score = score; + copyMap(copyMapFrom); + makelist(); + } + public void copyMap(Fish[][] copyfrom) { + for(int i=0; i<4; i++) { + for(int j=0; j<4; j++) { + Fish fish = new Fish(copyfrom[i][j].num, copyfrom[i][j].direct,i,j); + this.map[i][j] = fish; + } + } + } + + public void makelist() { + for(int i=0; i<4; i++) { + for(int j=0; j<4; j++) { + Fish fish = this.map[i][j]; + this.fish_li.add(fish); + } + } + Collections.sort(this.fish_li, new Comparator() { + public int compare(Fish o1, Fish o2) { + return o1.num-o2.num; + } + }); + } +} +public class _6_teenshark { + static int[] dx = {-1, -1, 0, 1, 1, 1, 0, -1}; + static int[] dy = {0, -1, -1, -1, 0, 1, 1, 1}; + static int maxscore = 0; + + public static boolean canSharkMove(Status status) { + int shark_x = status.shark_x; + int shark_y = status.shark_y; + int shark_di = status.shark_direct; + Fish[][] map = status.map; + // 해당 위치에 물고기 없음 OR 경계밖 + for(int i=1; i<=4; i++) { + int x = shark_x+dx[shark_di-1]*i; + int y = shark_y+dy[shark_di-1]*i; + if(x>3|| y>3 || x<0 || y<0) { // 이동 불가능 + continue; + } + if(map[x][y].num==-1) { + continue; + } + return true; + } + return false; + } +// public static boolean canFishMove(Fish f, Status status) { +// int x = f.x+dx[f.direct-1]; +// int y = f.y+dy[f.direct-1]; +// if(!(x>3|| y>3 || x<0 || y<0 ||(status.shark_x==x && status.shark_y==y))) { +// return true; +// } +// return true; +// } + public static void start(Status status) { + List fish_li = status.fish_li; + Fish[][] map = status.map; + if(!canSharkMove(status)) { + maxscore = maxscore 8) { + f.direct =1; + } + int x = f.x+dx[f.direct-1]; + int y = f.y+dy[f.direct-1]; + if(!(x>3|| y>3 || x<0 || y<0 || (status.shark_x==x && status.shark_y==y))) { + if(map[x][y].num==-1) { + f.x =x; + f.y =y; + map[x][y] = f; + + }else { + Fish change = map[x][y]; + Fish temp = map[f.x][f.y]; + map[f.x][f.y] = change; + map[x][y] = temp; + } + break; + }else { + continue; + } + } + } + + // 상어 이동 -> 후보생길때마다 새로운 status 만든다. + int shark_x = status.shark_x; + int shark_y = status.shark_y; + int shark_di = status.shark_direct; + + for(int i=1; i<=4; i++) { + int x = shark_x+dx[shark_di]*i; + int y = shark_y+dy[shark_di]*i; + if( x>3|| y>3 || x<0 || y<0) { // 이동 불가능 + continue; + } + if(map[x][y].num ==-1) { // 이동 불가능 + continue; + } + // 이동 가능 (분기점) + // 이동 전 새로운 Status 생성 -> 얘를 변환한다 + Status new_stat = new Status(x, y, shark_di, map, status.score); + // 이동 후 물고기 먹음 -> map, shark의 방향, fish_li, score변함 + new_stat.score += new_stat.map[x][y].num; + new_stat.map[x][y].num =-1; + new_stat.shark_direct = new_stat.map[x][y].direct; + start(new_stat); + } + + } + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + List fish_li = new ArrayList<>(); + Fish[][] map = new Fish[4][4]; + for(int i=0; i<4; i++) { + for(int j=0; j<4; j++) { + int ai = sc.nextInt(); + int bi = sc.nextInt(); + Fish fish = new Fish(ai, bi,i,j); + map[i][j] = fish; + } + } + + // 상어가 0,0에 있는 물고기 먹고 시작한다. + Fish firstfish = map[0][0]; + map[0][0].num = -1; + Status status = new Status(0, 0, firstfish.direct, map, firstfish.num); + start(status); + // (반복) 물고기 이동 -> 상어 이동 -> until 상어가 더이상 이동할 곳이 없을 때까지 + System.out.println(maxscore); + } +} + + + +``` + +### 느낀점 +- 처음에는 재귀함수를 이용해서 풀려고 했으나 시간 초과 뜸 +- DP문제는 memoization을 통해 시간을 단축시킨다. +- bottom up 방식이 DP문제 풀기에 조금더 편했다. \ No newline at end of file diff --git a/source/woodi/201023_Programmers_42895.md b/source/woodi/201023_Programmers_42895.md new file mode 100644 index 0000000..66c89e0 --- /dev/null +++ b/source/woodi/201023_Programmers_42895.md @@ -0,0 +1,44 @@ +# 프로그래머스 DP 문제 +- N으로 표현 + - https://programmers.co.kr/learn/courses/30/lessons/42895 + +## 풀이 방법 +### 문제 이해하기 +- 숫자 N과 number가 주어질 때, N과 사칙연산만 사용해서 number를 표현 할 수 있는 방법 중 N 사용횟수의 최솟값을 구하시오. + +### 문제 접근 방법 +- +### 구현 배경 지식 +- DP +### 접근 방법을 적용한 코드 +```java +import java.util.*; +class Solution { + static int answer =-1; + public int solution(int N, int number) { + dfs(N, number, 0,0); + return answer; + } + static void dfs(int N, int number, int cnt, int prev){ + int nn = N; + if(cnt>8){ + answer = -1; + return; + } + if(number == prev){ + if(answer ==-1 || answer>cnt){ + answer = cnt; + return; + } + } + for(int i=0; i<8-cnt; i++){ + dfs(N, number, cnt+i+1, prev-nn); + dfs(N, number, cnt+i+1, prev+nn); + dfs(N, number, cnt+i+1, prev*nn); + dfs(N, number, cnt+i+1, prev/nn); + nn = nn*10+N; + } + } +} +``` + diff --git a/source/woodi/201023_Programmers_43238.md b/source/woodi/201023_Programmers_43238.md new file mode 100644 index 0000000..fecdb77 --- /dev/null +++ b/source/woodi/201023_Programmers_43238.md @@ -0,0 +1,46 @@ +# 프로그래머스 이분탐색문제 +- 입국심사 + - https://programmers.co.kr/learn/courses/30/lessons/43238 + +## 풀이 방법 +### 문제 이해하기 +- 입국심사를 기다리는 사람 수 n +- int[] times : 각 심사관이 한 명을 심사하는데 걸리는 시간이 담긴 배열 +- 가장 앞에 서 있는 사람은 비어 있는 심사대로 가서 심사를 받을 수 있습니다. 하지만 더 빨리 끝나는 심사대가 있으면 기다렸다가 그곳으로 가서 심사를 받을 수도 있습니다. +- 모든 사람이 심사를 받는데 걸리는 최소 시간을 구하시오. + + +### 문제 접근 방법 +- 정답을 가정(mid)하고 조건(n)과 비교한다. -> 가장 n과 유사한 mid가 정답 +- mid 시간 이내에 심사 가능한 인원의 수 -> sum(mid/times[i])=result, n과 result 비교, n== result이면 mid 값을 반환한다. +### 구현 배경 지식 +- 이분탐색 +### 접근 방법을 적용한 코드 +```java +import java.util.*; + +class Solution { + public long solution(int n, int[] times) { + long answer = 0; + Arrays.sort(times); + long left =1; + long right = (long)n*times[times.length-1]; // 가장 오래 걸리는 경우 + while(left<=right){ + long mid = (left+right)/2; + long sum=0; // mid 시간 이내에 심사 가능한 인원의 수 + for(int t : times){ + sum+= (mid/t); + } + if(sum 이후 d[lastidx] 중 가장 큰 값이 answer이다 + + for(int i=0; i li = new ArrayList<>(); + for(int i=0; i q = new LinkedList<>(); + q.add(begin);// begin을 담는다. + d[d.length-1] = 0; +// System.out.println("begin: "+begin); + while(!q.isEmpty()){ + String now = q.remove(); +// System.out.println("now: "+now); + if(now.equals(target)) { + answer = d[li.indexOf(now)]; + break; + } + for(int i=0; imap[a+1][i]) {// 경사로 : \ + boolean slopePossible=true; + for(int t=a+2; ta-l; t--) { + if(t<0 || map[a][i]!=map[t][i]) { + slopePossible = false; + } + } + if(!slopePossible) { + result = false; + break; + }else { + a++; + } + } + }else if(Math.abs(map[a][i]-map[a+1][i])==0){ + a++; + }else { + result = false; + break; + } + } + return result; + } + public static boolean checkRow(int i) { // map[i][0~n-1] + boolean result= true; + int a=0; + while(amap[i][a+1]) {// 경사로 : \ + boolean slopePossible=true; + for(int t=a+2; ta-l; t--) { + if(t<0 ||map[i][a]!=map[i][t]) { + slopePossible = false; + } + } + if(!slopePossible) { + result = false; + break; + }else { + a++; + } + } + }else if(Math.abs(map[a][i]-map[a+1][i])==0){ + a++; + }else { + result = false; + break; + } + } + return result; + } + 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()); + l = Integer.parseInt(st.nextToken()); + map = new int[n][n]; + for(int i=0; i +- 첫째 줄) N: 격자 크기, M: 상어 마리 수 , K: 냄새가 사라지는데 걸리는 시간 +- 둘째 줄~N개의 줄 ) 0은 빈칸, 그 외 0이 아닌 수 x는 x번 상어가 들어가 있는 칸 +- 그 다음 줄 ) 각 상어의 방향 (1: 위, 2: 아래, 3: 좌, 4: 우) +- 그 다음 줄~) 각 상어의 방향 우선 순위 (하나의 상어당 4줄(현재 방향), 각 줄은 4개로 이루어짐(현재 방향에 따른 우선순위)) +- <출력> +- 1번 상어만 격자에 남게 되기까지 걸리는 시간 출력. 단, 1000초를 넘어갈 경우 -1 출력 + +### 문제 접근 방법 +- +### 구현 배경 지식 +- 시뮬레이션 +### 접근 방법을 적용한 코드 +```java +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.StringTokenizer; +public class AdultShark { + + static int[][][] priority; // 상어별 이동 우선순위 : 1(위), 2(아래), 3(좌), 4(우) + // priority[1] : 상어 1 + // priority[1][2] : 상어1의 현재 방향 (아래) + // priority[1][2][1] : 상어1의 현재 방향에서 첫번째 우선순위 + static int n; + static int m; + static int k; + static int[] di = {0, -1, 1, 0, 0}; + static int[] dj = {0, 0, 0, -1, 1}; + + public static boolean checkBound(int x, int y) { + if(x<0 || x>=n || y<0 || y>=n) { + return false; + } + return true; + } + + public static void move(int[][] map_smell, int[][] map_shark, int[] curr_direct, int[][] map_smell_own, int count_shark) { + int time = 0; + // count_shark + while(true) { + + // 1초 후 + time++; + // map_smell의 모든 숫자는 1씩 감소하고 +// for(int i=0; i0) +// map_smell[i][j]--; +// } +// } +// System.out.println("---------------------"); +// System.out.println(time); +// printMap(map_shark); +// System.out.println(); +// printSmell(map_smell); + // 모든 상어가 각자의 우선순위에 따라 움직임 + for(int i=0; i update curr_direct[shark_num] + if(checkBound(tempi, tempj) && map_smell[tempi][tempj]==0) { + curr_direct[shark_num] = next_direct; + flag = true; + break; + }else { // 그렇지 않으면 다음 p + continue; + } + } + // p를 다 돌았는데도 아무 냄새 없는 칸이 없어(flag==false) -> 자신의 냄새가 있는 칸으로 update curr_direct[shark_num] + + if(flag==false) { +// System.out.println("냄새 없는 칸이 없음. 자신의 냄새가 있는 곳 탐색"); + for(int p=1; p<=4; p++) { + int next_direct = priority[shark_num][curr_direct[shark_num]][p]; + int tempi = i+di[next_direct]; + int tempj = j+dj[next_direct]; + // tempi, tempj가 격자 밖으로 나가는지 검사하기 + // next_move 한 칸에 자신의 냄새가 있으면 -> update curr_direct[shark_num] + if(checkBound(tempi, tempj) && map_smell_own[tempi][tempj]==shark_num) { + curr_direct[shark_num] = next_direct; + flag = true; + break; + }else { // 그렇지 않으면 다음 p + continue; + } + } + } + } + } + } + // curr_direct에 따라 상어의 새로운 위치를 구하고 + + int[][] new_shark_map = new int[n][n]; + + for(int i=0; i 더 작은 숫자의 상어가 차지 한다. + int a = new_shark_map[ni][nj]; + if(shark0) + map_smell[i][j]--; + } + } + // 새로운 위치 중 겹치는 위치가 있으면 가장 숫자가 작은 상어가 차지한다. --> update map_shark + // (이때 사라지는 상어 수 만큼 count_shark-- 해준다.) + // 냄새를 뿌린다. --> update map_smell, map_smell_own + for(int i=0; i=1000) { + System.out.println(-1); + break; + } +// System.out.println(count_shark); + } + // while 문 끝남 -> 1번 샤크만 남았을 것 + } + public static int[][] copyMap(int[][] new_map_shark){ + int[][] map = new int[n][n]; + for(int i=0; i[] link에 담는다. +- link[i]에는 i번 컴퓨터와 연결된 다른 컴퓨터들의 번호가 ArrayList로 담긴다. +- BFS로 탐색한다. +### 구현 배경 지식 +- BFS +### 접근 방법을 적용한 코드 +```java +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +class Solution { + public int solution(int n, int[][] computers) { + int answer = 0; // network 개수 + ArrayList[] link = new ArrayList[n]; // 안해주면 object 형태로 저장 (generic) + for(int i=0; i(); + } + // 1. 연결된 컴퓨터 정보 담기 + for(int i=0; i q = new LinkedList<>(); + while(count!=n) { + System.out.println("count :"+count); + // 아직 방문 안한 노드 중 하나를 추가한다. + for(int i=0; i +- 첫째 줄) N: 격자 크기, M: 상어 마리 수 , K: 냄새가 사라지는데 걸리는 시간 +- 둘째 줄~N개의 줄 ) 0은 빈칸, 그 외 0이 아닌 수 x는 x번 상어가 들어가 있는 칸 +- 그 다음 줄 ) 각 상어의 방향 (1: 위, 2: 아래, 3: 좌, 4: 우) +- 그 다음 줄~) 각 상어의 방향 우선 순위 (하나의 상어당 4줄(현재 방향), 각 줄은 4개로 이루어짐(현재 방향에 따른 우선순위)) +- <출력> +- 1번 상어만 격자에 남게 되기까지 걸리는 시간 출력. 단, 1000초를 넘어갈 경우 -1 출력 + +### 문제 접근 방법 +- +### 구현 배경 지식 +- 시뮬레이션 +### 접근 방법을 적용한 코드 +```java +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.StringTokenizer; +public class AdultShark { + + static int[][][] priority; // 상어별 이동 우선순위 : 1(위), 2(아래), 3(좌), 4(우) + // priority[1] : 상어 1 + // priority[1][2] : 상어1의 현재 방향 (아래) + // priority[1][2][1] : 상어1의 현재 방향에서 첫번째 우선순위 + static int n; + static int m; + static int k; + static int[] di = {0, -1, 1, 0, 0}; + static int[] dj = {0, 0, 0, -1, 1}; + + public static boolean checkBound(int x, int y) { + if(x<0 || x>=n || y<0 || y>=n) { + return false; + } + return true; + } + + public static void move(int[][] map_smell, int[][] map_shark, int[] curr_direct, int[][] map_smell_own, int count_shark) { + int time = 0; + // count_shark + while(true) { + + // 1초 후 + time++; + // map_smell의 모든 숫자는 1씩 감소하고 +// for(int i=0; i0) +// map_smell[i][j]--; +// } +// } +// System.out.println("---------------------"); +// System.out.println(time); +// printMap(map_shark); +// System.out.println(); +// printSmell(map_smell); + // 모든 상어가 각자의 우선순위에 따라 움직임 + for(int i=0; i update curr_direct[shark_num] + if(checkBound(tempi, tempj) && map_smell[tempi][tempj]==0) { + curr_direct[shark_num] = next_direct; + flag = true; + break; + }else { // 그렇지 않으면 다음 p + continue; + } + } + // p를 다 돌았는데도 아무 냄새 없는 칸이 없어(flag==false) -> 자신의 냄새가 있는 칸으로 update curr_direct[shark_num] + + if(flag==false) { +// System.out.println("냄새 없는 칸이 없음. 자신의 냄새가 있는 곳 탐색"); + for(int p=1; p<=4; p++) { + int next_direct = priority[shark_num][curr_direct[shark_num]][p]; + int tempi = i+di[next_direct]; + int tempj = j+dj[next_direct]; + // tempi, tempj가 격자 밖으로 나가는지 검사하기 + // next_move 한 칸에 자신의 냄새가 있으면 -> update curr_direct[shark_num] + if(checkBound(tempi, tempj) && map_smell_own[tempi][tempj]==shark_num) { + curr_direct[shark_num] = next_direct; + flag = true; + break; + }else { // 그렇지 않으면 다음 p + continue; + } + } + } + } + } + } + // curr_direct에 따라 상어의 새로운 위치를 구하고 + + int[][] new_shark_map = new int[n][n]; + + for(int i=0; i 더 작은 숫자의 상어가 차지 한다. + int a = new_shark_map[ni][nj]; + if(shark0) + map_smell[i][j]--; + } + } + // 새로운 위치 중 겹치는 위치가 있으면 가장 숫자가 작은 상어가 차지한다. --> update map_shark + // (이때 사라지는 상어 수 만큼 count_shark-- 해준다.) + // 냄새를 뿌린다. --> update map_smell, map_smell_own + for(int i=0; i=1000) { + System.out.println(-1); + break; + } +// System.out.println(count_shark); + } + // while 문 끝남 -> 1번 샤크만 남았을 것 + } + public static int[][] copyMap(int[][] new_map_shark){ + int[][] map = new int[n][n]; + for(int i=0; i=5) { + d[5]=1; + } + + for(int i=6; i<=n; i++) { + if(d[i-5]>0) { + d[i]=d[i-5]+1; + }else if(d[i-3]>0) { + d[i]=d[i-3]+1; + } + } + + int answer; + if(d[n]==0) { + answer =-1; + }else { + answer = d[n]; + } + System.out.println(answer); + } +} + +``` + diff --git a/source/woodi/201226_programmers_43164.md b/source/woodi/201226_programmers_43164.md new file mode 100644 index 0000000..54078d6 --- /dev/null +++ b/source/woodi/201226_programmers_43164.md @@ -0,0 +1,111 @@ +# 프로그래머스 dfs +- 여행경로 + - https://programmers.co.kr/learn/courses/30/lessons/43164 + +## 풀이 방법 +### 문제 이해하기 +- 주어진 항공권을 '모두' 이용하여 여행경로를 짤때, 이 경로를 출력해라 +- 항상 "ICN" 공항에서 출발한다. +- [입력] + - 항공권 정보가 담긴 2차원 배열 tickets +- [출력] + - 방문하는 공항 경로를 1차원 배열에 담아 출력 +- [추가 조건] + - 모든 공항은 알파벳 대문자 3글자 + - 주어진 공항 수는 3개 이상 10,000개 이하 + - tickets의 각 행 [a,b]는 a공항에서 b공항으로 가는 항공권이 있다는 뜻 + - 주어진 항공권은 모두 사용해야함 + - 가능한 경로가 2개 이상일 경우, 알파벳 순서가 앞서는 경로를 return + - 모든 도시를 방문할 수 없는 경우는 주어지지 않음 + +### 문제 접근 방법 +- +### 구현 배경 지식 +- DFS +### 접근 방법을 적용한 코드 +```java +package programmers; + +import java.util.*; + +public class bfsdfs_43164 { + static ArrayList candidates; + public static void dfs(String depart, HashMap> map,String[] path, int i) { + // 출발지, 출발지에서 직통으로 갈수있는 도시 map, 현재까지 경로, 현재까지 경로 인덱스 + + // 더 이상 갈 수 있는 경로(도시)가 없는 경우 + if(map.get(depart)==null || map.get(depart).size()==0) { + // 마지막 도시까지 전부 방문. 즉, path를 다 채움 + if(i==path.length-1) { + String[] newpath = new String[path.length]; // 경로를 값만 복사 (깊은 복사) + // 복사를 안하고 candidates에 담으면 추후 path가 바뀔때마다 저장된 값도 바뀜(주소값이 저장되므로) + for(int t=0; t li = map.get(depart); + i++; + + // for문 돌면서 remove하기 위해 임시로 복사해둠 + ArrayList templi = new ArrayList<>(); + templi.addAll(li); + + for(String next:templi) { + // 해당 ticket을 사용했을 경우, li에서 지워버린 후 map에 저장 -> dfs + li.remove(next); + map.put(depart, li); + path[i] = next; + dfs(next,map,path,i); + // 백트래킹 + li.add(next); + map.put(depart, li); + } + } + public static String[] solution(String[][] tickets) { + String[] answer = {}; + HashMap> map = new HashMap<>(); + // key: 출발지, value : 출발지에서 직통으로 갈 수 있는 도시들 + candidates = new ArrayList<>(); + for(int i=0; i()); + map.get(tickets[i][0]).add(tickets[i][1]); + } + } + + String[] path = new String[tickets.length+1]; // 경로 + path[0] = "ICN"; + dfs("ICN", map, path,0); // 가능한 경로를 모두 candidates에 담는다. + + // 알파벳 순으로 정렬 + Collections.sort(candidates, new Comparator(){ + @Override + public int compare(String[] o1, String[] o2) { + return Arrays.toString(o1).compareTo(Arrays.toString(o2)); + } + }); + // 가장 앞에 있는 것이 답이다. + answer = candidates.get(0); + return answer; + } + + public static void main(String[] args) { + String[][] tickets1 = {{"ICN", "JFK"}, {"HND", "IAD"}, {"JFK", "HND"}}; + String[][] tickets2 = {{"ICN", "SFO"}, {"ICN", "ATL"}, {"SFO", "ATL"}, {"ATL", "ICN"}, {"ATL","SFO"}}; + String[][] tickets3 = {{"ICN", "A"}, {"ICN", "B"}, {"B", "ICN"}}; + String[] answer = solution(tickets3); + System.out.println("정답 : "+Arrays.toString(answer)); + } +} + + +``` + diff --git a/source/woodi/20200830_kakaoBlindTest01.md b/source/woodi/20200830_kakaoBlindTest01.md new file mode 100644 index 0000000..84f2689 --- /dev/null +++ b/source/woodi/20200830_kakaoBlindTest01.md @@ -0,0 +1,78 @@ +# 카카오 블라인드 테스트 문제 : 가사검색 +========================= +- https://www.welcomekakao.com/learn/courses/30/lessons/60060?language=java + +### 풀이 방법 +- 이진검색을 이용한 풀이(시간복잡도 : O(NM) --> O(M*logN)) (효율성 통과 못함) + +#### 문제 접근 방법 +- + +### 구현 배경 지식 +- 이진검색 + +#### 접근 방법을 적용한 코드 + +```java + + +class Solution { + public static class Music{ + String genre; + int musicNum; + int play; + public Music(String gen, int mn, int play) { + this.genre= gen; + this.musicNum =mn; + this.play = play; + } + } + static int count_by_array(String query, List words) { + int cnt=0; + for(Object w : words) { + String word = (String)w; + if(word.compareTo(query.replace("?", "a"))>0 && + word.compareTo(query.replace("?", "z"))<0) { + cnt++; + } + } + return cnt; + } + public static int[] sol2_binarySearch(String[] words, String[] queries) { + int[] answer = new int[queries.length]; + // 1. 먼저 word의 '길이'에 대해서 각각 리스트(혹은 배열)을 만들어 단어를 담아주고, 오름차순 정렬 + // 인덱스 : 가사의 길이 (가사 단어의 최대 길이 =10000) + List[] arrange_words = new ArrayList[10001]; + List[] reversed_words = new ArrayList[10001]; + + for(int i=0; i<10001; i++) { + arrange_words[i] = new ArrayList(); + reversed_words[i] = new ArrayList(); + } + for(String w : words) { + arrange_words[w.length()].add(w); + reversed_words[w.length()].add(new StringBuffer(w).reverse().toString()); + } + // 2. 정렬 + for(int i=0; i<10001; i++) { + Collections.sort(arrange_words[i]); + Collections.sort(reversed_words[i]); + } + // 3. 이진탐색 수행한다. + for(int i=0; i0) { + int temp = d[i-a[j]]; + if(temp < 1) { + continue; + }else { + if(d[i]<1) { + d[i] = temp+1; + }else { + d[i] = d[i] > temp+1 ? temp+1 : d[i]; + } + } + } + } + } + int answer; + if(d[k]<1) { + answer = -1; + }else { + answer = d[k]; + } + System.out.println(answer); + } +} + +``` + +### 느낀점 +- 처음에는 재귀함수를 이용해서 풀려고 했으나 시간 초과 뜸 +- DP문제는 memoization을 통해 시간을 단축시킨다. +- bottom up 방식이 DP문제 풀기에 조금더 편했다. \ No newline at end of file diff --git a/source/woodi/20201006_boj_17142.md b/source/woodi/20201006_boj_17142.md new file mode 100644 index 0000000..7daac11 --- /dev/null +++ b/source/woodi/20201006_boj_17142.md @@ -0,0 +1,204 @@ +# 백준 삼성 SW A형 문제 +- 연구소 3 + - https://www.acmicpc.net/problem/17142 + +## 풀이 방법 +### 문제 이해하기 +- 가장 처음에 모든 바이러스는 비활성 상태이고, +- 활성 상태인 바이러스는 상하좌우로 인접한 모든 빈 칸으로 동시에 복제되며, 1초가 걸린다. +- 바이러스 M개를 활성 상태로 변경하려고 한다. +- 연구소의 상태가 주어졌을때, 모든 '빈 칸'에 바이러스를 퍼뜨리는 최소 시간을 구하라 + +- 입력 : 0은 빈칸, 1은 벽, 2는 바이러스를 놓을 수 있는 위치 +- 출력 : 연구소의 모든 빈칸에 바이러스가 있게 되는 '최소 시간' +- <주의 사항> + - spreadTime시간을 비교할 때, map에서 0인 곳에서의 시간만을 비교해야 한다. + - 모든 바이러스가 활성화될 필요는 없다. 모든 빈칸에만 확산되면 됨. + +### 문제 접근 방법 +- 입력 값을 int[][] map에 담는다. +- d[r][c] : 좌표 (r,c)에 확산되기까지 걸리는 시간, 방문 안했을 시 0, 벽이면 -1 +- 그중에서 + - 0(공백) -> 개수를 센다 + - 2(바이러스) -> ArrayList에 담는다. +- combination() : 모든 바이러스 중에서 M개의 바이러스를 선택해서 활성화 시키기 (조합, 백트래킹) + 1. 바이러스 리스트의 크기(=바이러스 개수) 만큼 boolean[] check를 만들고 선택여부를 담는다. + 2. 선택에 순서를 부여하여 앞에서 부터 선택한다. (for문) + 3. M개 만큼 선택이 완료되면 BFS를 호출한다. + 4. BFS가 끝나고 반환하는 result 값에 대해서 전역변수와 비교하여 + 모든 경우의 수 중 최소값을 찾는다. + +- BFS() : 처음 선택된 바이러스를 큐에 담고 해당 위치의 d값을 1로 전환 -> bfs 시작 + - 노드를 꺼내고 좌,우, 위, 아래에 대해서 탐색한다. + - 아직 방문 안한 빈 공간 & 바이러스 : d[다음노드위치] = d[현재노드위치] + 1, 큐에 추가 + - 아직 방문 안한 빈 공간 : cnt+1, 최대값과 비교 + - 탐색이 끝나고 cnt값이 전체 빈공간의 개수와 같을 경우 최대값 리턴한다. + + + +### 구현 배경 지식 +- DFS를 이용한 조합 구하기 : 백트래킹 +- BFS +### 접근 방법을 적용한 코드 +```java + +import java.util.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +/* 입력 : 0은 빈칸, 1은 벽, 2는 바이러스를 놓을 수 있는 위치 + * 출력 : 연구소의 모든 빈칸에 바이러스가 있게 되는 '최소 시간' -> 최소시간 + * + * 주의!! spreadTime시간을 비교할 때, map에서 0인 곳에서의 시간만을 비교해야 한다. (2인 곳은 비교하면 안됨) + * */ + +class Index{ + int col; + int row; + public Index( int row, int col) { + this.row = row; + this.col = col; + } +} +public class BFS_17142 { + static int[] dcol = {1,-1,0,0}; + static int[] drow = {0,0,1,-1}; + static int[][] map; + static ArrayList virus = new ArrayList<>(); + static boolean [] check; + static int[][] d; + static int n; + static int m; + static int answer=Integer.MAX_VALUE; + static int COUNT; + 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()); // 심을 바이러스 개수 + map = new int[n][n]; // 0 : 빈칸, 1 : 벽 , 2 : 바이러스 위치 + d = new int[n][n]; + int cnt=0; + for(int r=0; r virus_case = copy(); + //선택된 바이러스 리스트를 깊은 복사하여 bfs한다. + int result = bfs(virus_case); // 바이러스 선택 정보와 함께 bfs + result-=1; + // result 중 -1이 아닌 최소 값이 answer가 됨 + // 모든 result가 -1이라면 answer는 바뀌지 않음 + if(result>0) { + answer = answer > result ? result : answer; + } + return; + } + // 하나가 선택되면(고정) 그 다음꺼부터 나머지를 고른다. + for(int i=start; i virus) { + int[][] d = deepcopy_d(); + int answer = 0; + int cnt=0; // 확산된 빈칸 수 + Queue q= new LinkedList<>(); + int max=0; + for(int i=0; i=0&&row=0) { + if(map[row][col]==0 && d[row][col]==0) { // 빈 공간이고 아직 방문 안한 상태면 + d[row][col] = d[now.row][now.col]+1; + q.add(new Index(row, col)); + cnt++; + max = max < d[row][col] ? d[row][col] : max; + }else if(map[row][col]==2 && d[row][col]==0) { + d[row][col] = d[now.row][now.col]+1; + q.add(new Index(row, col)); + } + } + } + } + + // 모든 빈칸이 확산되었다면 max 값을 찾는다. + if(cnt==COUNT) { + answer = max; + }else { // 모든 빈칸이 확산이 안됨 + answer = 0; + } + return answer; + } + + public static ArrayList copy(){ + ArrayList al = new ArrayList<>(); + for(int i=0; i{ + int start; + int end; + int weight; + + Edge(int start, int end, int weight){ + this.start = start; + this.end = end; + this.weight = weight; + } + @Override + public int compareTo(Edge o) { + // TODO Auto-generated method stub + return weight -o.weight; + } + +} + +public class boj_2887 { + static int[] parent; + static ArrayList edgeList; + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StringTokenizer st =null; + int n = Integer.parseInt(br.readLine()); // 행성의 개수 + + Node[] nodes = new Node[n]; + for(int i=0; i(); + + // x, y, z 각각에 대해서 정렬하고 각 행성의 번호화 비용을 edgeList에 추가 + Arrays.sort(nodes, (n1,n2)->n1.x-n2.x); + for(int i=0; in1.y-n2.y); + for(int i=0; in1.z-n2.z); + for(int i=0; i=N || y>=M) { + return true; + } + return false; + } + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StringTokenizer st = new StringTokenizer(br.readLine()); + M = Integer.parseInt(st.nextToken()); // y + N = Integer.parseInt(st.nextToken()); // x + K = Integer.parseInt(st.nextToken()); + List li = new ArrayList<>(); + Block[][] blockli = new Block[N][M]; + // 색칠한 영역 추가 + int lx=0,ly=0,rx=0,ry=0; + for(int i=0; i hm = new HashMap<>(); // + ArrayList ar = new ArrayList<>(); + int count=0; + // bfs 탐색 시작 + Queue q = new LinkedList(); + for(Block b : li) { + if(b.check) { +// System.out.println(b.x+", "+b.y); + continue; + } + q.add(b); + blockli[b.x][b.y].check=true; + int square=0; // 넓이 + while(!q.isEmpty()) { + Block now = q.remove(); + int x = now.x; + int y = now.y; + square++; + for(int i=0; i<4; i++) { + int nx = x+dx[i]; + int ny = y+dy[i]; + if(!isOutOfBound(nx, ny) && !blockli[nx][ny].check ) { + blockli[nx][ny].check=true; + q.add(blockli[nx][ny]); +// square++; + } + } + } +// debug(blockli); + count++; + ar.add(square); + } + + System.out.println(count); + Collections.sort(ar); + for(int a : ar) { + System.out.print(a+" "); + } + } +} + +``` + diff --git a/source/woodi/210320_boj_1931.md b/source/woodi/210320_boj_1931.md new file mode 100644 index 0000000..af57066 --- /dev/null +++ b/source/woodi/210320_boj_1931.md @@ -0,0 +1,105 @@ +# 백준 MST +- 회의실 배정 + - https://www.acmicpc.net/problem/1931 + +## 풀이 방법 +### 문제 이해하기 +- 하나의 회의실이 있음 +- N개의 회의에 대하여 회의실 사용표를 만들려고한다. +- 각 회의에 대해서는 시작시간과 끝나느 시간이 주어짐 +- 이때 각 회의가 겹치지 않게 하면서 회의실을 사용할 수 있는 회의의 최대 개수를 찾아라 +- (단, 회의의 시작 시간과 끝나는 시간이 같을 수도 있다.) + +### 문제 접근 방법 + +1. 목록 S에 남은 회의실 중 가장 일찍 끝나는 회의 Smin을 선택한다. +2. Smin과 겹치는 회의를 S에서 모두 지운다 +3. S가 텅 빌 때까지 반복한다. + +- 조건 '가장 종료 시간이 빠른 회의(Smin)를 포함하는 최적해가 반드시 존재한다.'가 성립. + +- 증명) + - S의 최적해 중 Smin을 포함하지 않는 답 (S')가 존재 + - 이 답(S')은 서로 겹치지 않는 회의의 목록임 + - S'에서 첫번째로 개최되는 회의를 지우고, Smin을 대신 추가해서 새로운 목록을 만든다. + - 이때, Smin은 S에서 가장 일찍 끝나는 회의이기 때문에 지워진 회의는 Smin보다 일찍 끝날 수 없다. + - 따라서 S'의 가장 첫번째 회의를 Smin으로 대체하여 새로 만든 목록 역시 최적해 중 하나이다. + +- 정리) + - S' = {a,b,c,d, ....} (a,b,c,d..는 서로 겹치지 않음) + - Smin의 끝나는 시간 < a의 끝나는시간 + - S'' = {Smin, b,c,d, ...} -> 마찬가지로 최적해 + + + +### 구현 배경 지식 +- Greedy +### 접근 방법을 적용한 코드 +```java +import java.util.ArrayList; +import java.util.PriorityQueue; +import java.util.Scanner; + +public class boj_1931 { + static class Node implements Comparable{ + + int s; + int e; + + public Node(int s, int e) { + this.s = s; + this.e = e; + } + + // 빨리 끝나는 순으로 정렬 + @Override + public int compareTo(Node o) { + if(this.e == o.e) { + return Integer.compare(this.s, o.s); + } + return Integer.compare(this.e, o.e); + } + } + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + int n = sc.nextInt(); + PriorityQueue pq = new PriorityQueue(); + for(int i=0; i pq.peek().s)) { + pq.remove(); + } + } + + System.out.println("result : "+ cnt); + } +} + + +``` + + +### 그리디 알고리즘 +#### 완전 탐색(혹은 DP)과 그리디 +1. 공통점 : (분할정복) 여러 조각으로 쪼개고, 각 단계마다 답의 한 부분을 만들어감 +2. 차이점 + - 완탐 : 모든 선택지를 고려해보고 그 중 전체 답이 가장 좋은 방법을 선택 + - 그리디 : 각 단계마다 '지금 당장' 좋은 방법만을 선택 (지금 선택이 나중에 어떤 영향을 미칠지 고려하지 않는다.) + +- 참고) + - 탐욕법으로 최적해를 찾을 수 있는 많은 문제들은 동적 계획법으로도 풀 수 있다. + - 탐욕법으로 최적해를 찾을 수 있다 = 지금 한 단계만을 고려해도 답을 찾을 수 있다. + - 따라서 동적 계획법은 모든 단계를 고려하므로 답이 틀릴 수가 없다. + - 그럼에도 그리디를 사용하는 이유는 동적 계획법에 필요한 메모리나 시간이 과도하게 크기 때문이다. \ No newline at end of file diff --git a/source/woodi/210327_programmers_42862.md b/source/woodi/210327_programmers_42862.md new file mode 100644 index 0000000..f4e5e74 --- /dev/null +++ b/source/woodi/210327_programmers_42862.md @@ -0,0 +1,88 @@ +# 프로그래머스 +- 체육복 + - https://programmers.co.kr/learn/courses/30/lessons/42862?language=java + +## 풀이 방법 +### 문제 이해하기 +- <입력> +- 전체 학생 수 n (2 <= n <= 30) +- 도난 학생 배열 lost (1 <= lost.length <= n) +- 여벌 학생 배열 reserve + +- <조건> +- 여벌 옷이 있는 학생 번호 +1, -1 한테만 빌려줄 수 있음 +- 여벌 옷이 있는 학생도 도난 가능, 이 경우 하나만 도난 당한다고 가정. 빌려 줄 수 없다. + +- 이때 체육 수업을 들을 수 있는 최대 학생 수를 구하라 + +### 문제 접근 방법 +- 전체 학생 배열 int[] student에 대하여 여벌 있으면 1, 도난 -1, 둘다 아니면 0, 여벌이 있으나 도난 당하면 0으로 초기화 +- student 배열을 for 문으로 돌면서 + - student[i] == -1 이면, + - 빌려 줄 수 있는 경우가 1개 + - student[i+1], student[i-1] 둘 중 하나 값이 1일 경우 -> answer++, student[i+1] or student[i-1] =0으로 set + - i가 1이거나 n인 경우 + - 빌려 줄 수 있는 경우가 2개 (student[i+1]==1 && student[i-1]==1) + - 앞에껄 빌려준다. 즉, student[i-1]을 먼저 빌려준다. (그리디) + - 뒤에꺼부터 빌려주면 나중에 도난 당한 사람이 못 빌릴 수도 있음 + - 아니면, answer++ +### 구현 배경 지식 +- 그리디 +### 접근 방법을 적용한 코드 +```java +class Solution { + public int solution(int n, int[] lost, int[] reserve) { + int answer = 0; + + int[] student = new int[n+1]; // 잃어버림 = -1, 여벌 = 1, 여벌있는데 잃어버림 =0, 둘다 아님 =0 + + for(int i=0; i lock의 홈 딱 맞으면 열림 + - 돌기와 돌기가 만나면 안됨 + - key는 lock 영역을 벗어날 수 있음 + - key는 회전, 이동 가능 + +### 문제 접근 방법 +- 90도씩 회전한 4가지 형태의 keys list 만들기 +- key가 lock 영역을 벗어나는 것 까지 포함하는 전체 영역 map 만든다 +- key를 90도씩 회전하며 모든 map 영역을 탐색한다. +### 구현 배경 지식 +- 완전탐색 +### 접근 방법을 적용한 코드 +```java +import java.util.*; +class Solution { + static int m; + static int n; + static int size; + public boolean solution(int[][] key, int[][] lock) { + + m = key.length; + n = lock.length; + // 회전한 모든 key의 경우의 수 담기 (총 4가지) + int[][][] keys = lotatedKeys(key); + + // map 영역 정의 + size = n+2*m-2; + int[][] map = new int[size][size]; + for(int i=m-1; i test 함수 (lock 영역의 모든 값이 1이면 true 리턴) + for(int t=0; t<4; t++) { + for(int i=0; i