728x90
반응형
SMALL

문제 설명

XX게임에는 피로도 시스템(0 이상의 정수로 표현합니다)이 있으며, 일정 피로도를 사용해서 던전을 탐험할 수 있습니다. 이때, 각 던전마다 탐험을 시작하기 위해 필요한 "최소 필요 피로도"와 던전 탐험을 마쳤을 때 소모되는 "소모 피로도"가 있습니다. "최소 필요 피로도"는 해당 던전을 탐험하기 위해 가지고 있어야 하는 최소한의 피로도를 나타내며, "소모 피로도"는 던전을 탐험한 후 소모되는 피로도를 나타냅니다. 예를 들어 "최소 필요 피로도"가 80, "소모 피로도"가 20인 던전을 탐험하기 위해서는 유저의 현재 남은 피로도는 80 이상 이어야 하며, 던전을 탐험한 후에는 피로도 20이 소모됩니다.

이 게임에는 하루에 한 번씩 탐험할 수 있는 던전이 여러개 있는데, 한 유저가 오늘 이 던전들을 최대한 많이 탐험하려 합니다. 유저의 현재 피로도 k와 각 던전별 "최소 필요 피로도", "소모 피로도"가 담긴 2차원 배열 dungeons 가 매개변수로 주어질 때, 유저가 탐험할수 있는 최대 던전 수를 return 하도록 solution 함수를 완성해주세요.

제한사항

  • k는 1 이상 5,000 이하인 자연수입니다.
  • dungeons의 세로(행) 길이(즉, 던전의 개수)는 1 이상 8 이하입니다.
    • dungeons의 가로(열) 길이는 2 입니다.
    • dungeons의 각 행은 각 던전의 ["최소 필요 피로도", "소모 피로도"] 입니다.
    • "최소 필요 피로도"는 항상 "소모 피로도"보다 크거나 같습니다.
    • "최소 필요 피로도"와 "소모 피로도"는 1 이상 1,000 이하인 자연수입니다.
    • 서로 다른 던전의 ["최소 필요 피로도", "소모 피로도"]가 서로 같을 수 있습니다.

입출력 예

k dungeons result

80 [[80,20],[50,40],[30,10]] 3

입출력 예 설명

현재 피로도는 80입니다.

만약, 첫 번째 → 두 번째 → 세 번째 던전 순서로 탐험한다면

  • 현재 피로도는 80이며, 첫 번째 던전을 돌기위해 필요한 "최소 필요 피로도" 또한 80이므로, 첫 번째 던전을 탐험할 수 있습니다. 첫 번째 던전의 "소모 피로도"는 20이므로, 던전을 탐험한 후 남은 피로도는 60입니다.
  • 남은 피로도는 60이며, 두 번째 던전을 돌기위해 필요한 "최소 필요 피로도"는 50이므로, 두 번째 던전을 탐험할 수 있습니다. 두 번째 던전의 "소모 피로도"는 40이므로, 던전을 탐험한 후 남은 피로도는 20입니다.
  • 남은 피로도는 20이며, 세 번째 던전을 돌기위해 필요한 "최소 필요 피로도"는 30입니다. 따라서 세 번째 던전은 탐험할 수 없습니다.

만약, 첫 번째 → 세 번째 → 두 번째 던전 순서로 탐험한다면

  • 현재 피로도는 80이며, 첫 번째 던전을 돌기위해 필요한 "최소 필요 피로도" 또한 80이므로, 첫 번째 던전을 탐험할 수 있습니다. 첫 번째 던전의 "소모 피로도"는 20이므로, 던전을 탐험한 후 남은 피로도는 60입니다.
  • 남은 피로도는 60이며, 세 번째 던전을 돌기위해 필요한 "최소 필요 피로도"는 30이므로, 세 번째 던전을 탐험할 수 있습니다. 세 번째 던전의 "소모 피로도"는 10이므로, 던전을 탐험한 후 남은 피로도는 50입니다.
  • 남은 피로도는 50이며, 두 번째 던전을 돌기위해 필요한 "최소 필요 피로도"는 50이므로, 두 번째 던전을 탐험할 수 있습니다. 두 번째 던전의 "소모 피로도"는 40이므로, 던전을 탐험한 후 남은 피로도는 10입니다.

따라서 이 경우 세 던전을 모두 탐험할 수 있으며, 유저가 탐험할 수 있는 최대 던전 수는 3입니다.

모든 가능한 던전 탐험 순서를 고려하고, 최대한 많은 던전을 탐험하는 순서를 찾는 것이다. 이러한 문제는 일반적으로 "백트래킹" 알고리즘을 사용하여 해결할 수 있다.

package LV2;

public class H87946 {
    // 최대 탐험 가능한 던전 수를 저장할 변수
    int answer = 0;

    public int solution(int k, int[][] dungeons) {
        // 각 던전을 방문했는지 여부를 체크하는 배열
        boolean[] visited = new boolean[dungeons.length];

        // 깊이 우선 탐색(DFS)를 시작
        dfs(k, dungeons, visited, 0);

        // 최대 탐험 가능한 던전 수를 반환
        return answer;
    }

    // 깊이 우선 탐색(DFS)을 수행하는 메소드
    private void dfs(int fatigue, int[][] dungeons, boolean[] visited, int count) {
        // 현재까지 탐험한 던전 수(count)와 answer 중 큰 값을 answer로 저장
        answer = Math.max(answer, count);

        // 모든 던전에 대해서
        for (int i = 0; i < dungeons.length; i++) {
            // 아직 방문하지 않은 던전이고, 현재 피로도가 던전의 최소 필요 피로도 이상인 경우
            if (!visited[i] && fatigue >= dungeons[i][0]) {
                // 해당 던전을 방문했음을 표시
                visited[i] = true;

                // 던전의 소모 피로도를 현재 피로도에서 빼고, 다음 던전을 탐험하기 위해 재귀 호출합
                dfs(fatigue - dungeons[i][1], dungeons, visited, count + 1);

                // 백트래킹: 던전 탐험을 마치고 해당 던전의 방문 상태를 원래대로 되돌린다.
                visited[i] = false;
            }
        }
    }
}
  1. **answer**를 0으로 설정하여 최대 던전 탐험 횟수를 기록한다.
  2. solution 함수는 입력된 피로도 **k**와 던전 정보를 받아서 dfs (깊이 우선 탐색)를 호출한다. 던전에 방문한 표시를 위한 visited 배열과 현재까지 탐험한 던전의 개수를 0으로 설정한다.
  3. dfs 함수는 현재 남은 피로도, 던전 정보, 방문한 던전의 상태, 그리고 현재까지 탐험한 던전의 수를 입력으로 받는다. 각각의 재귀 호출에서는 **answer**를 현재까지 탐험한 던전의 수와 비교하여 더 큰 값을 **answer**에 저장한다.
  4. **dfs**는 모든 던전에 대해 현재 던전을 탐험할 수 있는지 (즉, 현재 피로도가 최소 필요 피로도 이상인지, 그리고 해당 던전을 아직 방문하지 않았는지)를 확인한다. 만약 가능하다면, 해당 던전을 방문하고 남은 피로도를 업데이트한 후, 다음 던전을 탐험하기 위해 **dfs**를 재귀 호출한다. 이후 백트래킹을 위해 해당 던전의 방문 상태를 원래대로 되돌린다.
  5. 마지막으로 solution 함수는 최대 던전 탐험 횟수를 반환한다.
728x90
반응형
LIST

'알고리즘 > 프로그래머스 JAVA LV.2' 카테고리의 다른 글

빛의 경로 사이클  (0) 2023.05.25
전화번호 목록  (0) 2023.05.25
[1차]캐시  (0) 2023.05.25
연속 부분 수열 합의 개수  (0) 2023.05.25
괄호 회전하기  (0) 2023.05.24
728x90
반응형
SMALL

문제 설명

캐시

지도개발팀에서 근무하는 제이지는 지도에서 도시 이름을 검색하면 해당 도시와 관련된 맛집 게시물들을 데이터베이스에서 읽어 보여주는 서비스를 개발하고 있다.

이 프로그램의 테스팅 업무를 담당하고 있는 어피치는 서비스를 오픈하기 전 각 로직에 대한 성능 측정을 수행하였는데, 제이지가 작성한 부분 중 데이터베이스에서 게시물을 가져오는 부분의 실행시간이 너무 오래 걸린다는 것을 알게 되었다.

어피치는 제이지에게 해당 로직을 개선하라고 닦달하기 시작하였고, 제이지는 DB 캐시를 적용하여 성능 개선을 시도하고 있지만 캐시 크기를 얼마로 해야 효율적인지 몰라 난감한 상황이다.

어피치에게 시달리는 제이지를 도와, DB 캐시를 적용할 때 캐시 크기에 따른 실행시간 측정 프로그램을 작성하시오.

입력 형식

  • 캐시 크기(cacheSize)와 도시이름 배열(cities)을 입력받는다.
  • cacheSize는 정수이며, 범위는 0 ≦ cacheSize ≦ 30 이다.
  • cities는 도시 이름으로 이뤄진 문자열 배열로, 최대 도시 수는 100,000개이다.
  • 각 도시 이름은 공백, 숫자, 특수문자 등이 없는 영문자로 구성되며, 대소문자 구분을 하지 않는다. 도시 이름은 최대 20자로 이루어져 있다.

출력 형식

  • 입력된 도시이름 배열을 순서대로 처리할 때, "총 실행시간"을 출력한다.

조건

  • 캐시 교체 알고리즘은 LRU(Least Recently Used)를 사용한다.
  • cache hit일 경우 실행시간은 1이다.
  • cache miss일 경우 실행시간은 5이다.

입출력 예제

캐시크기(cacheSize) 도시이름(cities) 실행시간

3 ["Jeju", "Pangyo", "Seoul", "NewYork", "LA", "Jeju", "Pangyo", "Seoul", "NewYork", "LA"] 50
3 ["Jeju", "Pangyo", "Seoul", "Jeju", "Pangyo", "Seoul", "Jeju", "Pangyo", "Seoul"] 21
2 ["Jeju", "Pangyo", "Seoul", "NewYork", "LA", "SanFrancisco", "Seoul", "Rome", "Paris", "Jeju", "NewYork", "Rome"] 60
5 ["Jeju", "Pangyo", "Seoul", "NewYork", "LA", "SanFrancisco", "Seoul", "Rome", "Paris", "Jeju", "NewYork", "Rome"] 52
2 ["Jeju", "Pangyo", "NewYork", "newyork"] 16
0 ["Jeju", "Pangyo", "Seoul", "NewYork", "LA"] 25

https://school.programmers.co.kr/learn/courses/30/lessons/17680

 

주어진 캐시 크기와 도시 이름 배열에 따라 LRU 알고리즘을 적용하여 총 실행 시간을 계산한다. 여기서 캐시 히트의 경우 실행 시간은 1이고, 캐시 미스의 경우 실행 시간은 5이다.

import java.util.*;

public class Solution {
    public int solution(int cacheSize, String[] cities) {
        // 캐시 크기가 0이면, 모든 도시를 DB에서 가져와야 하므로 
        // 실행 시간은 도시의 수에 5를 곱한 값이다.
        if(cacheSize == 0)
            return cities.length * 5;
            
        // LinkedList를 사용하여 캐시를 구현
        // 이 데이터 구조는 캐시에 새 항목을 추가하거나 가장 오래된 항목을 제거하는 데 효율적이다.
        LinkedList<String> cache = new LinkedList<>();
        int time = 0; // 총 실행 시간을 저장하는 변수이다.
        for(int i=0; i<cities.length; i++){
            // 도시 이름은 대소문자를 구분하지 않으므로 모두 대문자로 변환
            String city = cities[i].toUpperCase();
            // 해당 도시가 캐시에 이미 있는지 확인
            if(cache.remove(city)){
                // 캐시에 도시가 있다면, 캐시 히트이다.
                // 캐시 히트일 경우 실행 시간은 1이므로 time을 1 증가시키고,
                // 해당 도시를 캐시의 맨 앞으로 이동
                cache.addFirst(city);
                time++;
            } else {
                // 캐시에 도시가 없다면, 캐시 미스이다.
                // 캐시 미스일 경우 실행 시간은 5이므로 time을 5 증가시킨다.
                int currentSize = cache.size();
                // 캐시가 이미 가득 차 있다면, 가장 오래된 항목을 제거한다.
                if(currentSize == cacheSize){
                    cache.pollLast();
                }
                // 캐시에 새 도시를 추가한다.
                cache.addFirst(city);
                time += 5;
            }
        }
        // 총 실행 시간을 반환
        return time;
    }
}
package LV2;

import java.util.LinkedList;

public class H17680 {
    public int solution(int cacheSize, String[] cities) {
        // 캐시 크기가 0이면, 모든 도시를 DB에서 가져와야 하므로
        // 실행 시간은 도시의 수에 5를 곱한 값이다.
        if(cacheSize == 0)
            return cities.length * 5;

        // LinkedList를 사용하여 캐시를 구현
        // 이 데이터 구조는 캐시에 새 항목을 추가하거나 가장 오래된 항목을 제거하는 데 효율적이다.
        LinkedList<String> cache = new LinkedList<>();
        int time = 0; // 총 실행 시간을 저장하는 변수이다.
        for(int i=0; i<cities.length; i++){
            // 도시 이름은 대소문자를 구분하지 않으므로 모두 대문자로 변환
            String city = cities[i].toUpperCase();
            // 해당 도시가 캐시에 이미 있는지 확인
            if(cache.remove(city)){
                // 캐시에 도시가 있다면, 캐시 히트이다.
                // 캐시 히트일 경우 실행 시간은 1이므로 time을 1 증가시키고,
                // 해당 도시를 캐시의 맨 앞으로 이동
                cache.addFirst(city);
                time++;
            } else {
                // 캐시에 도시가 없다면, 캐시 미스이다.
                // 캐시 미스일 경우 실행 시간은 5이므로 time을 5 증가시킨다.
                int currentSize = cache.size();
                // 캐시가 이미 가득 차 있다면, 가장 오래된 항목을 제거한다.
                if(currentSize == cacheSize){
                    cache.pollLast();
                }
                // 캐시에 새 도시를 추가한다.
                cache.addFirst(city);
                time += 5;
            }
        }
        // 총 실행 시간을 반환
        return time;
    }
}

 

  • 만약 캐시 크기가 0이면 모든 도시를 검색해야하므로 모든 도시에 대해 캐시 미스가 발생하고, 따라서 총 실행 시간은 도시 수에 5를 곱한 값이 된다.
  • 그렇지 않은 경우 LinkedList를 사용하여 캐시를 구현한다. 이 구조는 캐시에 새 항목을 추가하거나 가장 오래된 항목을 제거하는 데 효과적이다.
  • 각 도시에 대해 캐시에 이미 있는지 확인한다. 만약 존재한다면, 이는 캐시 히트이며 실행 시간을 1 증가시키고, 해당 도시를 캐시의 맨 앞으로 이동시킨다.
  • 캐시에 도시가 없으면 캐시 미스가 발생하므로 실행 시간을 5 증가시킨다. 캐시가 이미 가득 차 있으면 가장 오래된 항목을 제거하고 새 도시를 추가합니다. 캐시에 공간이 있다면 단순히 새 도시를 추가한다.
728x90
반응형
LIST

'알고리즘 > 프로그래머스 JAVA LV.2' 카테고리의 다른 글

전화번호 목록  (0) 2023.05.25
피로도  (0) 2023.05.25
연속 부분 수열 합의 개수  (0) 2023.05.25
괄호 회전하기  (0) 2023.05.24
점프와 순간 이동  (0) 2023.05.24
728x90
반응형
SMALL

문제 설명

철호는 수열을 가지고 놀기 좋아합니다. 어느 날 철호는 어떤 자연수로 이루어진 원형 수열의 연속하는 부분 수열의 합으로 만들 수 있는 수가 모두 몇 가지인지 알아보고 싶어졌습니다. 원형 수열이란 일반적인 수열에서 처음과 끝이 연결된 형태의 수열을 말합니다. 예를 들어 수열 [7, 9, 1, 1, 4] 로 원형 수열을 만들면 다음과 같습니다.

!https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/f207cd37-34dc-4cbd-96bb-83435bd6efd4/그림.png

원형 수열은 처음과 끝이 연결되어 끊기는 부분이 없기 때문에 연속하는 부분 수열도 일반적인 수열보다 많아집니다.

원형 수열의 모든 원소 elements가 순서대로 주어질 때, 원형 수열의 연속 부분 수열 합으로 만들 수 있는 수의 개수를 return 하도록 solution 함수를 완성해주세요.


제한사항

  • 3 ≤ elements의 길이 ≤ 1,000
  • 1 ≤ elements의 원소 ≤ 1,000

입출력 예

elements result

[7,9,1,1,4] 18

입출력 예 설명

입출력 예 #1

길이가 1인 연속 부분 수열로부터 [1, 4, 7, 9] 네 가지의 합이 나올 수 있습니다.

길이가 2인 연속 부분 수열로부터 [2, 5, 10, 11, 16] 다섯 가지의 합이 나올 수 있습니다.

길이가 3인 연속 부분 수열로부터 [6, 11, 12, 17, 20] 다섯 가지의 합이 나올 수 있습니다.

길이가 4인 연속 부분 수열로부터 [13, 15, 18, 21] 네 가지의 합이 나올 수 있습니다.

길이가 5인 연속 부분 수열로부터 [22] 한 가지의 합이 나올 수 있습니다.

이들 중 중복되는 값을 제외하면 다음과 같은 18가지의 수들을 얻습니다.

[1, 2, 4, 5, 6, 7, 9, 10, 11, 12, 13, 15, 16, 17, 18, 20, 21, 22]

 

import java.util.HashSet;

public class Solution {
    public int solution(int[] elements) {
        int n = elements.length;
        int[] sums = new int[2 * n];
        HashSet<Integer> uniqueSums = new HashSet<>();

        // 원형 수열을 구성하기 위해 elements를 sums 배열에 두 번 복사
        for (int i = 0; i < n; i++) {
            sums[i] = sums[i + n] = elements[i];
        }

        // 가능한 모든 연속 부분 수열의 합을 구하고, 이를 HashSet에 추가하여 중복을 제거
        for (int i = 0; i < n; i++) {
            int sum = 0;
            for (int j = i; j < i + n; j++) {
                sum += sums[j];
                uniqueSums.add(sum);
            }
        }
        
        // 최종적으로, 연속 부분 수열의 합으로 만들 수 있는 유일한 값의 개수를 반환
        return uniqueSums.size();
    }
}
package LV2;

import java.util.HashSet;

public class H131701 {
    public int solution(int[] elements) {
        int n = elements.length;
        int[] sums = new int[2 * n];
        HashSet<Integer> uniqueSums = new HashSet<>();

        // 원형 수열을 구성하기 위해 elements를 sums 배열에 두 번 복사
        for (int i = 0; i < n; i++) {
            sums[i] = sums[i + n] = elements[i];
        }

        // 가능한 모든 연속 부분 수열의 합을 구하고, 이를 HashSet에 추가하여 중복을 제거
        for (int i = 0; i < n; i++) {
            int sum = 0;
            for (int j = i; j < i + n; j++) {
                sum += sums[j];
                uniqueSums.add(sum);
            }
        }

        // 최종적으로, 연속 부분 수열의 합으로 만들 수 있는 유일한 값의 개수를 반환
        return uniqueSums.size();
    }
}

첫 번째 단계에서는 원형 수열의 모든 부분 수열의 합을 찾는다.
두 번째 단계에서는 중복 항목을 제거하여 유일한 합의 개수를 찾는다.

728x90
반응형
LIST

'알고리즘 > 프로그래머스 JAVA LV.2' 카테고리의 다른 글

피로도  (0) 2023.05.25
[1차]캐시  (0) 2023.05.25
괄호 회전하기  (0) 2023.05.24
점프와 순간 이동  (0) 2023.05.24
예상 대진표  (0) 2023.05.24

+ Recent posts