From d220b68e569af8c74c6557c11d750fdb4489c8b2 Mon Sep 17 00:00:00 2001 From: chaewonjeong Date: Mon, 12 May 2025 21:40:59 +0900 Subject: [PATCH 1/2] =?UTF-8?q?1=EB=B2=88=20/=20--=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\354\240\225\354\261\204\354\233\220.java" | 392 ++++++++++++++++++ 1 file changed, 392 insertions(+) create mode 100644 "4\354\243\274\354\260\250/1. \353\257\270\354\203\235\353\254\274 \354\227\260\352\265\254/1\353\262\210_\354\240\225\354\261\204\354\233\220.java" diff --git "a/4\354\243\274\354\260\250/1. \353\257\270\354\203\235\353\254\274 \354\227\260\352\265\254/1\353\262\210_\354\240\225\354\261\204\354\233\220.java" "b/4\354\243\274\354\260\250/1. \353\257\270\354\203\235\353\254\274 \354\227\260\352\265\254/1\353\262\210_\354\240\225\354\261\204\354\233\220.java" new file mode 100644 index 0000000..298f646 --- /dev/null +++ "b/4\354\243\274\354\260\250/1. \353\257\270\354\203\235\353\254\274 \354\227\260\352\265\254/1\353\262\210_\354\240\225\354\261\204\354\233\220.java" @@ -0,0 +1,392 @@ +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.*; +import java.util.stream.Collectors; + +class Main { + static int[][] dirs = new int[][] { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 } }; + + /** + * <------------------ 풀이 후 ------------------> + * 저번주 문제 중 좌표를 문자열로 해시맵에다 기록하는 방식이 생각나서 똑같이 풀어봤는데 + * 정렬등을 할때 매번 문자를 int배열로 다시 바꾼다던지하는 비용 발생 + * 좌표들의 갯수가 많지 않아 굳이 해시로 검색 할 필요도 없을듯 + * 직관적이긴 한데 생각보다 복잡... 다음엔 클래스로 빼는게 더 나을꺼 같음... + * 덕분에 스트림, 함수형 Map 연습 많이함 + */ + + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StringTokenizer st = new StringTokenizer(br.readLine()); + //StringBuilder result = new StringBuilder(); + + int N = Integer.parseInt(st.nextToken()); // 배양용기 크기 + // N 이 최대 15 + int Q = Integer.parseInt(st.nextToken()); // 실험횟수 + // Q 가 최대 50 + + // case 별로 bfs 완탐해도 시간초과는 없을 듯 + + int[][] board = new int[N][N]; + HashMap> microbes = new HashMap<>(); + // 크기는 HashSet 사이즈로 찾으면 됨 + //HashMap microbesSize = new HashMap<>(); + + // 미생물은 사각형으로 (r1, c1) (r2, c2)로 주어짐 + for (int q = 0; q < Q; q++) { + st = new StringTokenizer(br.readLine()); + int r1 = Integer.parseInt(st.nextToken()); + int c1 = Integer.parseInt(st.nextToken()); + int r2 = Integer.parseInt(st.nextToken()); + int c2 = Integer.parseInt(st.nextToken()); + int microbeType = q + 1; + // 1. 투입 + // << 투입되는 미생물을 저장할 자료구조 >> + // 미생물 무리의 크기, 미생물 무리의 고유번호(투입 순서, q), + // 배열은 0으로 초기화 -> 미생물 인덱스는 1부터 + // 현재 배양요기의 상태를 표기할 board 2차원 3차원 4차원...?? + insertMicrobe(board, microbeType, microbes, r1, c1, r2, c2); +// System.out.println("insert : " + microbeType); +// printBoard(board); + + // 겹치는 영역은 나중에 투입 된 녀석으로 대체 + // 겹치는 영역 판별은 어떻게?? + // 겹칠라면 우측하단 (ra2, ca2) 의 각 값들이 무조건 (rb1, cb1)보다 크다 + // 그 크기는 ?? -> (ra2 - rb1 + 1) * (ca2 - cb1 + 1) + checkMicrobeArea(board, microbes); +// System.out.println("check : " + microbeType); +// printBoard(board); + + + // 영역이 나뉘면 나뉜 지역 삭제 -> 모든 영역 bfs -> 중복 영역일시 해당 영역은 삭제 -> 중복영역 판별은 어떻게?? (해시맵등을 쓰고 val 값에다 bfs 시작위치를 작성) + // 시작 위치를 받아서 삭제하는 함수 구현 + + + // 2. 배양용기 이동 + // 크기가 가장 큰 무리를 이동 -> 미생물 무리의 정보를 저장할 자료구조가 필요.. 영역을 String으로 다 저장할까... < Q - "0,0" , "0,1" , ...> : HashMap> - > get(q).size() + // 새로 이동할 board를 완탐하면서 미생물 무리의 val 값을 탐색하면서 boar에 넣을 수 있는지 확인 만약 끝까지 갔는데도 못했다면 삭제 + // 보드를 완탐하는 기준은 r과 y가 작은 순 + // 그런 위치가 2개 이상일때는 어떻게하지..? cur의 r,c를 두고 무조건 끝까지 가봐야하나... + board = moveMicrobe(board, microbes); +// System.out.println("move : " + microbeType); +// printBoard(board); + + + // 3. 결과 기록 + // 인접한 무리쌍끼리의 곱 + // bfs 해서 인접한 type 쌍을 기록 + // 해당 type들의 size로 곱을 하면 될 듯 + calculateResult(board, microbes); + + } +// printBoard(board); +// printMap(microbes); + + + } + + static void insertMicrobe(int[][] board, int microbeType, HashMap> microbes, int r1, int c1, int r2, int c2) { + for (int i = r1; i < r2; i++) { + for (int j = c1; j < c2; j++) { + String coordinate = toStringCoordinate(i, j); + + if (board[i][j] != 0) { + int preType = board[i][j]; + + microbes.computeIfPresent(preType, (k,set) -> { + set.remove(coordinate); + return set.isEmpty() ? null : set; + }); + } + board[i][j] = microbeType; + microbes.computeIfAbsent(microbeType, k -> new HashSet<>()).add(coordinate); + } + } + } + + private static void checkMicrobeArea(int[][] board, HashMap> microbes) { + boolean[][] visited = new boolean[board.length][board.length]; + + HashMap> startPoint = new HashMap<>(); + + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board.length; j++) { + // 미생물도 있고 방문한적도 x + if(board[i][j] != 0 && !visited[i][j]) { + int microbeType = board[i][j]; + startPoint.computeIfAbsent(microbeType, k -> new ArrayList<>()).add(new int[]{i, j}); + + Queue queue = new ArrayDeque<>(); + queue.add(new int[]{i, j}); + visited[i][j] = true; + + while(!queue.isEmpty()) { + int[] cur = queue.poll(); + int row = cur[0]; + int col = cur[1]; + + for(int[] dir : dirs) { + int newRow = row + dir[0]; + int newCol = col + dir[1]; + + if(newRow >= 0 && newRow < board.length && newCol >= 0 && newCol < board.length && !visited[newRow][newCol] && board[newRow][newCol] == microbeType) { + queue.add(new int[]{newRow, newCol}); + visited[newRow][newCol] = true; + } + } + } + + } + + } + } + + for(Map.Entry> entry : startPoint.entrySet()) { + int microbeType = entry.getKey(); + ArrayList points = entry.getValue(); + + if(points.size() > 1) { // 2부분이상 나눠졌다면 + for(int[] point : points) { + deleteMicrobe(board, microbeType, point); + } + microbes.remove(microbeType); // 해당 미생물 삭제 + } + } + + + } + + static void deleteMicrobe(int[][] board, int microbeType, int[] startPoint) { + Queue queue = new ArrayDeque<>(); + + queue.add(new int[]{startPoint[0], startPoint[1]}); + board[startPoint[0]][startPoint[1]] = 0; + + + while(!queue.isEmpty()) { + int[] cur = queue.poll(); + int row = cur[0]; + int col = cur[1]; + + for(int[] dir : dirs) { + int newRow = row + dir[0]; + int newCol = col + dir[1]; + + if(newRow >= 0 && newRow < board.length && newCol >= 0 && newCol < board.length && board[newRow][newCol] == microbeType) { + queue.add(new int[]{newRow, newCol}); + board[newRow][newCol] = 0; + } + } + } + + } + + public static int[][] moveMicrobe(int[][] board, HashMap> microbes) { + // 크기가 가장크고 같을 경우 입력순서가 빠른 microbeType 부터 이동시키기위한 큐 생성 + Queue microbeQueue = microbes.entrySet().stream() + .sorted(Comparator.comparing((Map.Entry> e) -> e.getValue().size()).reversed() + .thenComparingInt(Map.Entry::getKey)) + .map(Map.Entry::getKey) + .collect(Collectors.toCollection(ArrayDeque::new)); + + int[][] newBoard = new int[board.length][board.length]; + + while(!microbeQueue.isEmpty()) { + int microbeType = microbeQueue.poll(); + //if(!microbes.containsKey(microbeType))continue; + + boolean isMoved = false; + + //System.out.println("debug : " + microbeType); //debug + + // set 중 좌하단(배열상 좌상단) 좌표 구하기 + ArrayList sortedCoordinates = sortingCoordinatesSet(microbes.get(microbeType)); + int[] basePoint = toIntCoordinate(sortedCoordinates.get(0)); + + outer: + for(int i = 0; i < board.length; i++) { + for(int j = 0; j < board.length; j++) { + boolean isValid = true; + // 매 루프에서는 아래 보드로 비교 -> 입력후 안되는 경우 그냥 버리면 됨 + int[][] tempBoard = cloneBoard(newBoard); + + if(tempBoard[i][j] != 0) continue; // 이미 미생물이 있다면 어짜피 그칸에서 시작 불가 + + // 상대적 차이를 구하고 평행이동 + int diffRow = basePoint[0] - i; + int diffCol = basePoint[1] - j; + + ArrayList moved = sortedCoordinates.stream() + .map((String str) -> toIntCoordinate(str)) + .map(o -> { + int row = o[0] - diffRow; + int col = o[1] - diffCol; + return new int[]{row, col}; + }) + .collect(Collectors.toCollection(ArrayList::new)); + + for (int[] m : moved) { + int row = m[0]; + int col = m[1]; + + if(row >= 0 && row < board.length && col >= 0 && col < board.length && tempBoard[row][col] == 0) { + tempBoard[row][col] = microbeType; + } else { + isValid = false; + break; + } + } + + // moved를 모두 순회 했다면 + if(isValid) { + // newBoard에다가 temp 복사 + newBoard = tempBoard; + // microbes 의 좌표정보 수정 + microbes.put(microbeType, moved.stream().map((int[] cor) -> toStringCoordinate(cor)).collect(Collectors.toCollection(HashSet::new))); + + isMoved = true; + break outer; + } + } + } + + if(!isMoved){ + microbes.remove(microbeType); + } + + } + + return newBoard; + } + + static void calculateResult(int[][] board, HashMap> microbes) { + HashSet adjMicrobes = new HashSet<>(); + int result = 0; + for(Map.Entry> entry : microbes.entrySet()) { + int microbeType = entry.getKey(); + int[] startPoint = toIntCoordinate(new ArrayList<>(entry.getValue()).get(0)); + boolean[][] visited = new boolean[board.length][board.length]; + + Queue queue = new ArrayDeque<>(); + queue.add(startPoint); + visited[startPoint[0]][startPoint[1]] = true; + + while(!queue.isEmpty()) { + int[] cur = queue.poll(); + int row = cur[0]; + int col = cur[1]; + + for(int[] dir : dirs) { + int newRow = row + dir[0]; + int newCol = col + dir[1]; + + if(newRow >= 0 && newRow < board.length && newCol >= 0 && newCol < board.length && board[newRow][newCol] != 0 && !visited[newRow][newCol]){ + if(board[newRow][newCol] == microbeType) { + queue.add(new int[]{newRow, newCol}); + visited[newRow][newCol] = true; + } else { + int nearMicrobeType = board[newRow][newCol]; + int a = microbeType; + int b = nearMicrobeType; + + if(a > b){ + int temp = a; + a = b; + b = temp; + } + + adjMicrobes.add(a+","+b); + } + } + } + } + } + + + if(!adjMicrobes.isEmpty()) { + for(String adjMicrobe : adjMicrobes) { + int[] adj = toIntCoordinate(adjMicrobe); + result += microbes.get(adj[0]).size() * microbes.get(adj[1]).size(); + } + } + System.out.println(result); + //debug +// for(String adjMicrobe : adjMicrobes) { +// System.out.print(adjMicrobe + " "); +// } +// System.out.println(); + + } + + + + // 유틸 함수 + static String toStringCoordinate(int r, int c) { + return r+","+c; + } + + static String toStringCoordinate(int[] cor) { + return cor[0]+","+cor[1]; + } + + static int[][] cloneBoard(int[][] board) { + int[][] newBoard = new int[board.length][board.length]; + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board.length; j++) { + newBoard[i][j] = board[i][j]; + } + } + return newBoard; + } + + static ArrayList sortingCoordinatesSet(HashSet coordinates) { + ArrayList sortedCoordinates = coordinates.stream() + .map(cor -> toIntCoordinate(cor)) + .collect(Collectors.toCollection(ArrayList::new)); + + return sortedCoordinates.stream() + .sorted(Comparator.comparing((int[] i) -> i[0]) + .thenComparing(i -> i[1])) + .map(i -> toStringCoordinate(i)) + .collect(Collectors.toCollection(ArrayList::new)); + } + + + static int[] toIntCoordinate(String coordinate) { + int[] result = new int[2]; + String[] split = coordinate.split(","); + result[0] = Integer.parseInt(split[0]); + result[1] = Integer.parseInt(split[1]); + return result; + } + + static void printMap(HashMap> microbes) { + for(Map.Entry> microbe : microbes.entrySet()){ + int k = microbe.getKey(); + HashSet set = microbe.getValue(); + System.out.print(k + " : "); + for (String coordi : set) { + System.out.print(coordi+ " "); + } + System.out.println(); + } + } + + static void printBoard(int[][] board) { + int size = board.length; + StringBuilder result = new StringBuilder(); + for(int i = 0; i < size; i++) { + for(int j = 0; j < size; j++) { + result.append(board[i][j]).append(" "); + } + result.append("\n"); + } + System.out.println(result); + } + + + + +} From 0874fff8f9525f90a9c5b31ff6e7fd8c362df819 Mon Sep 17 00:00:00 2001 From: chaewonjeong Date: Wed, 14 May 2025 17:39:25 +0900 Subject: [PATCH 2/2] =?UTF-8?q?2=EB=B2=88=20/=20--=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\354\240\225\354\261\204\354\233\220.java" | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 "4\354\243\274\354\260\250/2. \354\227\254\354\231\225 \352\260\234\353\257\270/2\353\262\210_\354\240\225\354\261\204\354\233\220.java" diff --git "a/4\354\243\274\354\260\250/2. \354\227\254\354\231\225 \352\260\234\353\257\270/2\353\262\210_\354\240\225\354\261\204\354\233\220.java" "b/4\354\243\274\354\260\250/2. \354\227\254\354\231\225 \352\260\234\353\257\270/2\353\262\210_\354\240\225\354\261\204\354\233\220.java" new file mode 100644 index 0000000..c69d042 --- /dev/null +++ "b/4\354\243\274\354\260\250/2. \354\227\254\354\231\225 \352\260\234\353\257\270/2\353\262\210_\354\240\225\354\261\204\354\233\220.java" @@ -0,0 +1,140 @@ +package codetree.삼성_2025_상반기_오후; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.*; + + +public class Main { + public static void main(String[] args) throws IOException { + // time limit : 3초 + // memory limit : 272 MB + // 기대 출력 -> 정찰명령(400) -> 보고받는 시간 출력 + + // 입력 + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StringTokenizer st; + + int Q = Integer.parseInt(br.readLine()); + + HashMap map = new LinkedHashMap<>(); + map.put(0, 0); + int idx = 1; + + for (int t = 0; t < Q; t++) { + st = new StringTokenizer(br.readLine()); + int cmd = Integer.parseInt(st.nextToken()); + + if (cmd == 100) { // 마을 건설 + int N = Integer.parseInt(st.nextToken()); + for (int i = 0; i < N; i++) { + int x = Integer.parseInt(st.nextToken()); // 좌표 + map.put(idx++, x); + } + + } else if (cmd == 200) { //개미집 건설 + int x = Integer.parseInt(st.nextToken()); // 건설 위치 x + map.put(idx++, x); + + } else if (cmd == 300) { // 개미집 철거 + int q = Integer.parseInt(st.nextToken()); + map.remove(q); + + } else if (cmd == 400) { + int r = Integer.parseInt(st.nextToken()); + + ArrayList list = new ArrayList<>(map.keySet()); + + // 이분 탐색 + int left = 0; + int right = 1000000000; + + // 시간의 최소를 구해야함 + while (left <= right) { + int mid = (left + right) / 2; + int count = 0; // 개미 수 + + int lastPos = -0x3f3f3f3f; // 첫번째 칸에는 무조건 들어가야하기 때문에 + + for(int i = 1; i < list.size(); i++) { + int curHouse = map.get(list.get(i)); + + if(curHouse - lastPos > mid){ + count++; + lastPos = curHouse; + } + } + + if (r >= count) { + // 문제에서 제시한 마리수보다 mid 값을 만족시키기위한 개미수가 작다면 mid 값을 더 줄여봐도됨 + right = mid - 1; + } else { + left = mid + 1; + } + } + System.out.println(left); + + } + + } + + + // 1. 마을 건설 + // 여왕 -> 0 + // 개미집 : 1부터 N + // 오름차순으로 주어짐 + + + // 2. 개미집 건설 + // 개미집 위치는 p -> 이전 건설된 위치보다 더 큰 (먼 좌표) + // k 번째 건설 명령 + // 해당 개미집 번호는 N + k + + + // 3. 개미집 철거 + // 철거 대상 : q -> q번 개미집 철거해라 + // ( q 번이 없는 경우 없으) + + + // 4. 개미집 정찰 (핵심) + // 정찰을 나갈 개미의 수 r + // 선택 -> 일개미들을ㄹ 추발 위치를 선택 + // 각 개미들을 매초 x 값이 1 증가하는 방향으로 이동 + // 개미가 지나는 집 (처음 선택 집 포함)은 안전한 개미집, 여왕 개미집은 항상 안전한 개미집 + // 각 개미들은 처음 만나는 개미집이 안전한 개미집인 경우에는 이동 중지 + // 개미들은 처음 개미집 선택 시, 정찰에 걸리는 시간이 최소가 되도록 적절히 선택 + + // 생각 나는 자료구조 -> 배열, 연결리스트 정도 아니면 개미집들과 거리를 맵으로... + // r 개의 포인터(개미) 들이 전체 리스트를 순회하는데 걸리는 시간이 최소가 되는 위치를 잡아야함 + // 추가적으로 리스트내의 값들에 빈번한 수정이 발생함 + + // 1. parameterSearch? 각 개미집들 사이의 거리를 이동시간으로 두고 : 아닌거같음.. 개미의 수를 구하는 것도 아니고 결과적으로 개미의 위치를 알아야하기 때문에.. + // 2. 그리디? -> 개미집을 선택하는 그리디한 방법이 있나? + // 생각나는 그리디 풀이 -> 각 개미집 사이의 거리를 구간이라 했을때 그 구간의 길이가 가장 큰 곳부터 제외(개미 배치) 시키는 방식 + + // 전체 시간 = 구간 A + 구간 B + 구간 C + // 가장 긴 구간을 먼저 제외시키는 것이 결과적으로 제일 최선의 선택인가?? + // 구간 B가 최대라고 하고 만약 구간 B를 제외 시키지 않고 구간 C나 A를 제외 시켜서는 + // 절대 전체 시간이 B를 제외시킨것보다 짧아 질 수 없음 + + // 해보니깐 잘못된 방식... + // 처음 조건에서 x 좌표의 범위가 1000000000 인걸 못보고 10000으로 착각함 범위가 10억인걸 봤으면 이분탐색을 바로 생각했을듯 + // 개미집의 위치가 중요한 변수이고 이걸 구하는 그리디한 방법을 떠오렸는데 정당하지 않은듯 + // 그리디 풀이는 한번해보고 안되면 손절하자 + // + // parametric search로 푸는게 맞음 개미의 출발위치를 정확히 알아야하고 그걸 결정하는게 중요한줄 알았는데 아니였음 개미위치보다는 r 개의 개미로 탐색할 수 있는 최소 시간을 구하고 + // 개미집의 수가 최대 10000 개이니깐 충분히 탐색해볼만 함 + // 최적화 : r 개의 개미로 탐색할 수 있는 최소 시간 + // 결정 : 시간이 x 일때, 필요한 개미가 r 개 이상인가 아닌가 + // 결정함수 -> 주어진 시간을 몇개의 개미로 커버가능한지 확인하고 개미의 수가 주어진 r 보다 크다면 시간을 늘리고 작거나 같다면 시간을 더 줄여보면 됨 + + + + // 3. 개미집 간의 간격/정찰개미 수/... 최소시간을 저장할 dp 테이블을 만들 수 있나 + + + // 정찰시 무조건 1번에는 개미가 있어야함 (개미는 x 가 증가하는 방향으로 나아감) + + } +}