일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- replace()
- Stack
- toUpperCase
- hash
- HashMap
- StringTokenizer
- Java
- dp
- map
- 코틀린기초
- 프로그래머스 java
- ac 5430번
- HashSet
- mysql hy000 에러
- kotlin
- 백준 1647번 도시 분할 계획 - java
- 백준 1806번 부분합 java
- 백준 1197번 최소 스패닝 트리 - java
- 백준 3190번
- 백준 1043번 거짓말 - java 분리 집합
- 최소 힙 1927
- 백준 2467번 용액 자바 - 이분탐색
- 백준 1541
- 프로그래머스
- append
- 18111번 마인크래프트 - java 구현
- 프로그래머스 자바
- StringBuilder
- 백준 14938번 서강그라운드
- 백준 2473번 세 용액 - java
- Today
- Total
말하는 컴공감자의 텃밭
알고리즘 재활 4일차 - 백준 2805, 15686번 본문
빨리 골드문제 더 풀어야하는데,,
나무 자르기
상근 교수님.. 잘 계시죠..?
나무의 h 높이 이상 부분을 자른 부분을 상근이가 갖게되는데,
이 자르는 높이의 최대값을 구해보는게 문제이다.
대충 나무가 이렇게 가지각색의 길이로 주어진다면 내가 자른 윗부분들의 합이 M이상이어야 한다.
이분 탐색으로 저 높이를 찾아보자.
먼저 이분탐색을 low와 hight 또는 left right 등 최소와 최대를 두어주고, 그 가운데 mid 값을 조정하여
값을 찾는 알고리즘이다.
우리가 숫자 up & down 게임을 할때 1~100이라면 50부터 불러서 줄이는것과 같은 로직이다.
만약 4가 정답이라면 1~100까지 하나하나 물어보는게 아닌
100 ▶ 50 ▶ 25 ▶ 12 ▶ 6 ▶ 3 ▶ 4 이런식으로 7번만에 빠르게 찾아낼 수 있다.
시간 복잡도는 (N) 에서 (logN)이 된다. 대부분의 이분탐색 활용문제는 시간때문이다.
로직 흐름
0과 나무 중 가장 높은 값을 더해 /2로 나누어 중간을 찾는다. ( 범위 0 ~ 최대값 )
해당 중간값으로 잘라 나무의 합을 구한다.
자른 나무의 값이 M보다 크다면 0 ~ 최대값 에서 mid+1 ~ 최대값으로 범위를 바꿔준다.
만약 나무가 부족하다면 0 ~ Mid - 1으로 범위를 바꿔 탐색한다.
만약 초기 min가 0, max가 10일때 mid는 5이다.
따라서 5의 높이로 나무들을 잘라준다.
자른 나무의 합이 요구사항보다 크다면 길이를 5보다 높혀도 되므로,
mid + 1 ~ 10인 6 ~ 10로 범위를 정해 mid 를 8로 높혀준다.
자른 나무의 합이 요구사항보다 적다면 길이를 5보다 줄여야 하므로,
0 ~ mid -1 인 0 ~ 4로 범위를 정해 mid를 2로 낮춘다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | import java.io.*; import java.util.*; public class Main { // S2 나무 자르기 static StringBuilder sb = new StringBuilder(); public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); // input int N = Integer.parseInt(st.nextToken()); int M = Integer.parseInt(st.nextToken()); int answer = 0; int [] trees = new int [N]; st = new StringTokenizer(br.readLine()); for (int i = 0; i < N; i++) { trees[i] = Integer.parseInt(st.nextToken()); } //logic int low = 0; int high = Arrays.stream(trees).max().getAsInt(); while (low <= high) { // Mid가 곧 우리가 찾는 숫자를 찾는 과정. int mid = (low + high) / 2; long sum = 0; // 10억 까지므로 long // 자른 나무 합 더해주기 for (int tree : trees) { if (tree > mid) { sum += tree - mid; } } // 만약 나무를 덜 잘라도 되는 경우 if (sum >= M) { answer = mid; // mid 값 저장 low = mid + 1; } else { high = mid - 1; } } System.out.println(answer); } } | cs |
치킨 배달
문제 이해가 잘 안갔었다.
정리해보면 가장 효율적인 치킨집과 집사이의 거리를 치킨거리라고 정의한다.
계산방식은 |r1-r2| + |c1-c2| 이며, M개의 치킨집으로 치킨거리의 최소값을 구하는 문제다.
구현문제이다 보니 간단하게 작성을 하고 시작했다.
- 먼저 반복을 통해 모든 경우의 수를 따져 M의 개수만큼 가게 지우기
- 남은 가게 좌표 정리
- 집이랑 가게 좌표 거리 합
- 최소값 출력
이렇게 풀었을때 예제는 해결되었으나 가지치기를 안해서 시간초과가 나고 말았다.
불필요한 0의 공간도 탐색을 했기 때문이다.
따라서 집과, 치킨집의 좌표를 리스트에 넣어 보관해주고, 그 좌표들로만 서로 탐색하게 해서 해결할 수 있었다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | import java.io.*; import java.util.*; public class Main { // G5 나무 자르기 static int[][] map; static boolean[] chk; static int N, M, answer; static List<int[]> bbq; static List<int[]> home; static StringBuilder sb = new StringBuilder(); public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); // input N = Integer.parseInt(st.nextToken()); M = Integer.parseInt(st.nextToken()); answer = Integer.MAX_VALUE; map = new int[N][N]; bbq = new ArrayList<>(); home = new ArrayList<>(); for (int i = 0; i < N; i++) { st = new StringTokenizer(br.readLine()); for (int j = 0; j < N; j++) { map[i][j] = Integer.parseInt(st.nextToken()); // 치킨집과 집의 좌표를 입력단계에서 저장 if (map[i][j] == 2) { bbq.add(new int[]{i, j}); }else if(map[i][j] == 1){ home.add(new int[]{i, j}); } } } // boolean으로 M개의 치킨집 고려 chk = new boolean[bbq.size()]; dfs(0, 0); System.out.println(answer); } // M개의 치킨집만 남기기 -> 모든 경우의 수 public static void dfs(int start, int cnt) { if (cnt == M) { int totalDistance = getChickenLength(); answer = Math.min(answer, totalDistance); return; } for (int i = start; i < bbq.size(); i++) { chk[i] = true; dfs(i + 1, cnt + 1); chk[i] = false; } } private static int getChickenLength() { int totalDistance = 0; for (int[] house : home) { int minDistance = Integer.MAX_VALUE; for (int i = 0; i < bbq.size(); i++) { if (chk[i]) { int[] chicken = bbq.get(i); //좌표 계산 int distance = Math.abs(house[0] - chicken[0]) + Math.abs(house[1] - chicken[1]); minDistance = Math.min(minDistance, distance); } } totalDistance += minDistance; } return totalDistance; } } | cs |
'알고리즘 > Backjoon - Java' 카테고리의 다른 글
알고리즘 재활 6일차 - 백준 2217, 13305, 1541번 (0) | 2024.08.08 |
---|---|
알고리즘 재활 5일차 - 백준 27648, 26043, 2817번 (0) | 2024.08.02 |
알고리즘 재활 3일차 - 백준 15652, 2636번 (0) | 2024.07.31 |
알고리즘 재활 2일차 - 백준 2751, 10814, 11726, 1074번 (0) | 2024.07.30 |
알고리즘 재활 1일차 - 백준 1620, 10828, 1764번 (0) | 2024.07.29 |