알고리즘

탐욕법 Greedy Algorithms 기본 개념 | [프로그래머스] 체육복 | 코딩테스트 자바 풀이

김휴고 2023. 8. 17. 12:43

탐욕법

  • 문제 해결 과정에서 그 순간순간마다 최적이라고 생각되는 결정을 하는 방식으로 최종 해답에 도달
  • 각 순간의 최선 ≠ 전체의 최선
  • 모든 해를 검토하는 방법(동적 계획법 등)에 비해 계산 속도가 빠르기 때문에, 몇몇 문제에서는 최적해를 빠르게 산출할 수 있음
  • 빠른 계산 속도가 장점으로 동적 계획법과 서로 보완적으로 활용

탐욕법을 언제 쓰나요?

  • 탐욕스러운 선택 조건 : 독립성 - 앞의 선택이 이후의 선택에 영향을 주지 않는다
  • 최적 구분 구조 조건 : 문제에 대한 최종 해결 방법이 부분 문제에서도 최적 해결 방법이다.

 

[프로그래머스] 체육복

문제링크 : https://school.programmers.co.kr/learn/courses/30/lessons/42862?language=java

 

import java.util.*;

class Solution {
    public int solution(int n, int[] lost, int[] reserve) {
        // 여벌 체육복 -> 빌려준다
        // 빌려주는 것 -> 앞뒤로만 빌려줄 수 있다 (index)
        // 앞-뒤 (독립, 전체-부분) -> 이 조건이 없으면
        // 완전탐색 문제 1번 -> 끝까지...
        // -> 가능한 많은 사람들이 체육복을 빌려야한다
        // 전체 학생의 수 n
        // 도난 당한 학생들 lost
        // 여분 reserve (여분은 1개씩 밖에 없음)
        // 도난 당했다면 여분은 의미 없음
        System.out.println(String.format("전체 학생의 수 : %d", n));
        System.out.print("도난 당한 학생들 : ");
        for (int l : lost) {
            System.out.print(l + " ");
        }
        System.out.println();
        System.out.print("여분 있는 학생들 : ");
        for (int r : reserve) {
            System.out.print(r + " ");
        }
        System.out.println();
        // 현재 자리 배치 먼저 생각해야함 (자리별로, 도난-여분을 다 정리하고 나서...)
        // int[] seat = new int[n+1]; // 1부터 n까지 인덱스를 사용 -> 0으로 다 대입되어 있는 배열
        int[] seat = new int[n+2]; // 맨 뒷자리도 뒤를 탐색할 수 있게
        for (int l : lost) {
            // l -> 잃어버린 학생들의 자리 번호 -> index로 사용해줄 수 있음
            seat[l]--; // 1개씩 잃어버린 걸 반영함
        }
        System.out.println("체육복 현황");
        for (int s : seat) {
            System.out.print(s + " ");
        } // 0 0 -1 0 -1 0 
        for (int r : reserve) {
            seat[r]++; // 1개의 여분을 반영
        }
        System.out.println();
        System.out.println("체육복 현황");
        for (int s : seat) {
            System.out.print(s + " ");
        } // 0 1 -1 1 -1 1 -> 상쇄를 배열에 반영하고 시작하고 싶은 것. 
        System.out.println();
        // 체육복이 있어서 0 이상의 값을 유지한 학생들을 탐색
        int answer = 0; // 참여할 수 있는 학생
        for (int i = 1; i <= n; i++) { // 향상된 for문이 아니라 일반 인덱스로 해야할까?
						// 실제 자리가 있는 n명 탐색
            // 1부터 탐색 -> 0은 자리만 만들어둔 것이므로...
            // 내가 -1이라면 앞뒤로 검색을 해서, 0으로 만들어주는 작업
            // System.out.println(String.format("%d의 체육복 소지 여부 : %d", i, seat[i]));
            if (seat[i] == -1) { // 체육복이 없으면
                System.out.println(String.format("%d는 체육복이 없습니다", i));
                // 앞에부터 여분이 있는지 검색을 해봐요
                if (seat[i - 1] == 1) { // 앞자리 학생이 여분이 있다면
                    System.out.println(String.format("%d는 앞자리에게 체육복을 빌렸습니다", i));
                    seat[i - 1]--;
                    seat[i]++;
                    answer++; // 빌렸으니까 참석 가능
                    continue;
                } // 이 이후는 앞자리에는 여분 없는 경우만 남음
                if (seat[i + 1] == 1) { // 뒷자리 학생이 여분이 있다면
                    System.out.println(String.format("%d는 뒷자리에게 체육복을 빌렸습니다", i));
                    seat[i + 1]--;
                    seat[i]++;
                    answer++; // 빌렸으니까 참석 가능
                }
            } else { // 체육복이 있으면
                answer++; // 체육복이 이미 있음
            }
        }
        System.out.println("체육복 현황");
        for (int s : seat) {
            System.out.print(s + " ");
        }
        System.out.println();
        return answer;
    }
}

 

제출용

import java.util.*;

class Solution {
    public int solution(int n, int[] lost, int[] reserve) {
        // int[] seat = new int[n+1];
        int[] seat = new int[n+2]; // 맨끝자리도 뒤를 탐색할 수 있게
        for (int l : lost) {
            seat[l]--;
        }
        for (int r : reserve) {
            seat[r]++;
        }
        int answer = 0;
        // for (int i = 1; i < seat.length; i++) {
        for (int i = 1; i <= n; i++) { // n명까지만 탐색 (앞뒤 한자리씩이 더 있음)
            if (seat[i] == -1) { // 체육복이 없으면
                if (seat[i - 1] == 1) {
                    seat[i - 1]--;
                    seat[i]++;
                    answer++;
                    continue;
                }
                if (seat[i + 1] == 1) {
                    seat[i + 1]--;
                    seat[i]++;
                    answer++;
                }
            } else {
                answer++;
            }
        }
        return answer;
    }
}

 

머메이드

sequenceDiagram
    participant Solution
    participant intArrayUtils
    participant Arrays

    Solution->>+intArrayUtils: int 배열 생성 (seat)
    loop lost 배열 초기화
        Solution->>intArrayUtils: lost 배열 원소 값을 seat 배열에 -1로 설정
    end
    loop reserve 배열 초기화
        Solution->>intArrayUtils: reserve 배열 원소 값을 seat 배열에 +1로 설정
    end

    Solution->>intArrayUtils: answer 변수 초기화
    loop 1부터 n까지 반복
        alt 체육복이 없으면
            Solution->>intArrayUtils: seat[i] 값 확인
            intArrayUtils->>intArrayUtils: 왼쪽 값 확인
            alt 왼쪽 값이 1이면
                Solution->>intArrayUtils: seat[i-1] 값 확인
                Solution->>intArrayUtils: seat[i], seat[i-1] 값 변경
                Solution->>Solution: answer 증가
            end
            intArrayUtils->>intArrayUtils: 오른쪽 값 확인
            alt 오른쪽 값이 1이면
                Solution->>intArrayUtils: seat[i+1] 값 확인
                Solution->>intArrayUtils: seat[i], seat[i+1] 값 변경
                Solution->>Solution: answer 증가
            end
        else 체육복이 있으면
            Solution->>Solution: answer 증가
        end
    end

    Solution->>+Arrays: answer 값 반환
    Arrays->>-Solution: 반환된 answer 값