제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers 알고리즘 고득점 Kit입니다.

포인트

다리를 큐로 표현해 각 트럭의 상태를 관리하며, 트럭이 다리를 건너는 동안의 시간과 다리의 무게 제한을 고려해 순차적으로 트럭을 다리에 올리고 내립니다. 반복문은 트럭이 모두 다리를 건널 때까지 계속됩니다.

 

 

소스코드

import java.util.*;

class Solution {
    static class Truck {
        int weight, position;

        public Truck(int weight) {
            this.weight = weight;
            this.position = 0;
        }

        public Truck(int weight, int position) {
            this.weight = weight;
            this.position = position;
        }
    }
    
    public int solution(int bridge_length, int weight, int[] truck_weights) {
        int time = 0; // 경과 시간
        int bridgeWeight = 0; // 현재 다리 위의 트럭 총 무게
        Queue<Truck> bridge = new LinkedList<>(); // 다리 위의 트럭 큐
        int idx = 0; // 대기 트럭 인덱스

        while (idx < truck_weights.length || !bridge.isEmpty()) {
            time++;

            if (!bridge.isEmpty()) {
                Truck frontTruck = bridge.peek(); // 맨 앞의 트럭
                if (time - frontTruck.position >= bridge_length) { // 다리를 다 건넜을 경우
                    bridgeWeight -= frontTruck.weight; // 다리 무게에서 제외
                    bridge.poll(); // 트럭 다리에서 내림
                }
            }

            if (idx < truck_weights.length) {
                if (bridgeWeight + truck_weights[idx] <= weight && bridge.size() < bridge_length) {
                    bridge.add(new Truck(truck_weights[idx], time)); // 트럭 다리에 올림
                    bridgeWeight += truck_weights[idx]; // 현재 다리 무게 갱신
                    idx++; // 대기 트럭 인덱스 증가
                }
            }
        }
        return time;
    }
}

코드 설명

 

  • Truck 클래스
    • weight: 트럭의 무게.
    • position: 트럭이 다리를 건너기 시작한 시점(시간).
  • 변수 초기화:
    • time: 전체 시뮬레이션의 경과 시간을 나타냅니다.
    • bridgeWeight: 현재 다리 위에 있는 트럭들의 총 무게입니다.
    • bridge: 현재 다리를 건너고 있는 트럭들을 저장하는 큐입니다.
    • idx: 대기 중인 트럭을 가리키는 인덱스입니다.
  • 메인 로직:
    • 시간 증가: 매 반복마다 time이 증가합니다. 이는 시뮬레이션의 한 단계를 의미합니다.
    • 트럭 이동:
      • bridge 큐에서 맨 앞에 있는 트럭(frontTruck)의 위치를 확인합니다.
      • time - frontTruck.position이 bridge_length보다 크거나 같다면, 트럭은 다리를 건넜으므로 bridge.poll()로 큐에서 제거하고, bridgeWeight에서 트럭의 무게를 뺍니다.
    • 새 트럭 추가:
      • 아직 다리를 건너지 않은 트럭이 있을 경우(idx < truck_weights.length), 현재 다리의 무게(bridgeWeight + truck_weights[idx])가 제한을 초과하지 않고 다리 길이 조건을 만족하면 새 트럭을 bridge에 추가합니다.
      • 새 트럭이 다리에 추가되면 bridgeWeight를 업데이트하고 idx를 증가시켜 다음 트럭을 가리킵니다.
  • 종료 조건:
    • 대기 중인 트럭이 더 이상 없고(idx >= truck_weights.length), bridge에 남아 있는 트럭도 없을 때 시뮬레이션이 종료됩니다.
  • 결과 반환:
    • 최종적으로 경과된 time을 반환합니다. 이는 모든 트럭이 다리를 건너기까지 걸린 총 시간입니다.

 

제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers 알고리즘 고득점 Kit입니다.

포인트

시간 복잡도 O(N^2)으로 잘 관리하면 문제가 패스하는 것을 확인했습니다. 하지만 시간복잡도를 줄이고 싶어 Stack을 이용하였고, O(N)의 시간 복잡도를 갖는 코드를 구현하였습니다.

소스코드

import java.util.*;

class Solution {
    public int[] solution(int[] prices) {
        int len = prices.length;
        int[] ans = new int[len];
        Stack<Integer> stack = new Stack<>();
        
        // 각 시간의 인덱스를 스택에 저장
        for (int i = 0; i < len; i++) {
            // 현재 가격이 스택의 마지막 가격보다 낮으면, 하락 시간 계산
            while (!stack.isEmpty() && prices[i] < prices[stack.peek()]) {
                int idx = stack.pop();
                ans[idx] = i - idx; // 하락 시간 계산
            }
            stack.push(i); // 현재 인덱스를 스택에 추가
        }
        
        // 남아 있는 인덱스들은 끝까지 가격이 떨어지지 않음
        while (!stack.isEmpty()) {
            int idx = stack.pop();
            ans[idx] = len - 1 - idx;
        }
        
        return ans;
    }
}

코드 설명

  • 루프를 통해 각 시점의 가격을 순회합니다.
    • 스택의 크기와 현재 가격이 마지막 주식 가격보다 낮으면
      • 스택에서 해당 인덱스를 꺼내고, 그 하락 시간을 i - idx로 계산하여 ans 배열에 저장.
    • 현재 시점 i를 스택에 추가하여 이후 가격 변화에 대해 추적.
  • 스택에 남아있는 시점들은 가격이 떨어지지 않은 것이므로 전체 크기에 대해 배열에 저장합니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers 알고리즘 고득점 Kit입니다.

포인트

프로세스의 최댓값과 그 갯수를 효율적으로 관리하는게 포인트였습니다. Map으로도 관리할 수 있고, 리스트로도 관리할 수 있겠지만
저는 이 문제에서 우선순위 큐를 사용하였습니다.

소스코드

import java.util.*;

class Solution {
    
    static class Process {
        int priority, index;
        public Process(int priority, int index) {
            this.priority = priority;
            this.index = index;
        }
    }
    
    public int solution(int[] priorities, int location) {
        Queue<Process> processes = new LinkedList<>();
        PriorityQueue<Integer> remainPriority = new PriorityQueue<>(Collections.reverseOrder());
        
        int len = priorities.length;
        
        for(int i = 0; i < len; i++) {
            int priority = priorities[i];
            remainPriority.add(priority);
            processes.add(new Process(priority, i));
        }
        
        int order = 1;
        while(true) {
            Process ready = processes.poll();
            int maxPriority = remainPriority.poll();
            if (maxPriority == ready.priority) {
                if (location == ready.index) {
                    return order;
                } else {
                    order++;
                }
            } else {
                remainPriority.add(maxPriority);
                processes.add(ready);
            }
        }
    }
}

코드 설명

  • static class Process
    우선순위와 고유번호를 저장하기 위해 static class Process를 선언합니다.
  • PriorityQueue<Integer> remainPriority = new PriorityQueue<>(Collections.reverseOrder());
    priority를 저장할 우선순위 큐를 선언합니다.
    • 해당 우선순위큐는 최소 힙이 아닌 최대 힙으로 선언하기 위해 Collections.reverseOrder()을 사용합니다.
  • processes 큐에서 프로세스를 하나씩 꺼내 처리합니다.
  • remainPriority에서 가장 높은 우선순위를 가져옵니다.
  • 현재 프로세스의 우선순위가 가장 높은 우선순위와 같으면:
    • location에 있는 프로세스인지 확인합니다.
    • 맞다면 현재 순서(order)를 반환합니다.
    • 아니라면 실행 순서를 증가시킵니다.
  • 현재 프로세스의 우선순위가 가장 높지 않으면:
    • 프로세스를 다시 큐에 넣고 remainPriority에 추가하여 다음 순서를 준비합니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers 알고리즘 고득점 Kit입니다.

포인트

유명한 스택 문제입니다. ')'이 있으면 '('가 스택에 존재해야합니다. 또 마지막 스택에는 아무런 원소가 없어야합니다.

소스코드

import java.util.Stack;

class Solution {
    boolean solution(String s) {
        Stack<Character> bracketValidator = new Stack<>();
        
        int len = s.length();
        for (int i = 0; i < len; i++) {
            if (s.charAt(i) == ')') {
                if (bracketValidator.isEmpty() || bracketValidator.peek() != '(') {
                    return false;
                }
                bracketValidator.pop();
            } else {
                bracketValidator.add('(');
            }
        }
        
        return bracketValidator.isEmpty() ? true : false;
    }
}

코드 설명

  • Stack<Character> bracketValidator = new Stack<>();
    Character를 원소로 갖는 Stack을 선언합니다.
  • if (s.charAt(i) == ')') { if (bracketValidator.isEmpty() || bracketValidator.peek() != '(') { return false; } }
    • ')'가 되었을 때 Stack에 (이 없거나 empty이면 false를 리턴합니다.
    • 이후 Stack의 원소를 pop해줍니다.
  • else { bracketValidator.add('('); }
    • '(' 값을 만났을 때에는 Stack에 add 해줍니다.
  • return bracketValidator.isEmpty() ? true : false;
    • Stack의 원소가 없으면 true를 리턴 그렇지 않으면 false를 리턴합니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers 알고리즘 고득점 Kit입니다.

포인트

선행 작업이 끝나야 뒤에 작업을 배포할 수 있습니다. 따라서 이전 기능 개발이 끝나는 날을 기록하여 해당 기간까지 다음 기능개발을 끝내는 수 있는지 확인하여 분기처리를 진행합니다.

소스코드

import java.util.*;

class Solution {
    
    public int[] solution(int[] progresses, int[] speeds) {
        List<Integer> production = new ArrayList<>();
        
        int len = progresses.length;
        int curDay = 0;
        int features = 0;
        
        for (int i = 0; i < len; i++) {
            // 남은 작업일 계산
            int remainDay = (100 - progresses[i]) / speeds[i];
            if ((100 - progresses[i]) % speeds[i] != 0) {
                remainDay++;
            }
            
            // 현재 기능이 배포 가능일보다 오래 걸리면 새로운 배포
            if (curDay < remainDay) {
                if (features > 0) {
                    production.add(features); // 이전에 쌓인 기능 수를 추가
                }
                curDay = remainDay; // 현재 배포 일자 갱신
                features = 1; // 새 기능 초기화
            } else {
                // 현재 기능이 이전 배포 일자 내에 완성되면 함께 배포
                features++;
            }
        }
        
        // 마지막에 남은 기능 수 추가
        if (features > 0) {
            production.add(features);
        }
        
        // 결과 배열로 변환
        return production.stream().mapToInt(Integer::intValue).toArray();
    }
}

코드 설명

  • remainDay = 현재 기능을 완료하기까지 걸리는 일 수.
    curDay = 지난 일자
    features = 배포 기능 개수
  • int remainDay = (100 - progresses[i]) / speeds[i];
    • 남은 작업 일을 계산합니다. 나누어 떨어지지 않는다면 하루가 더 필요합니다.
// 현재 기능이 배포 가능일보다 오래 걸리면 새로운 배포
if (curDay < remainDay) {
    if (features > 0) {
        production.add(features); // 이전에 쌓인 기능 수를 추가
    }
    curDay = remainDay; // 현재 배포 일자 갱신
    features = 1; // 새 기능 초기화
} else {
    // 현재 기능이 이전 배포 일자 내에 완성되면 함께 배포
    features++;
}

// 마지막에 남은 기능 수 추가
if (features > 0) {
    production.add(features);
}
  • 지금까지 걸린 기간에서 남은 일보다 작으면 현재 기능을 배포할 수 없습니다. 
    따라서 현재 기간까지 배포 가능한 기능 수(features)를 add해주고
    현재 배포 일자와 새 기능 수를 갱신합니다.
  • 반대로 걸린 기간이 남은 일자보다 더 크면 배포 기능 수를 추가해줍니다.
  • 이후 마지막에 남은 기능 수를 추가해줍니다.
  • return production.stream().mapToInt(Integer::intValue).toArray();
    • 결과를 int[] 배열로 반환합니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers 알고리즘 고득점 Kit입니다.

포인트

연속적으로 나타나는 숫자를 파악하기 위해 list나 배열에 index를 통해 확인할 수 있지만
LIFO구조의 Stack이라는 자료구조를 통해 더 효율적으로 문제를 풀 수 있다.

소스코드

import java.util.*;

public class Solution {
    
    public int[] solution(int[] arr) {
        Stack<Integer> ans = new Stack<>();
        
        for (int num: arr) {
            if (ans.isEmpty() || ans.peek() != num) {
                ans.add(num);
            } 
        }
        
        return ans.stream().mapToInt(Integer::intValue).toArray();
        
    }
}

코드 설명

  • Stack<Integer> ans = new Stack<>();
    • Integer 타입을 저장하는 스택을 선언한다.
  • if (ans.isEmpty() || ans.peek() != num)
    • 스택이 비어있거나 마지막으로 넣은 값과 연속적이지 않으면 스택에 값을 넣는다.
  • return ans.stream().mapToInt(Integer::intValue).toArray();
    • int[] 배열로 반환한다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers 알고리즘 고득점 Kit입니다.

포인트

두번의 정렬을 이용합니다. 장르 별로 정렬을 이용하고, 장르 내에 앨범 별로 정렬을 이용합니다. 

소스코드

import java.util.*;

class Solution {
    static class Album implements Comparable<Album>{
        int idx, playCnt;

        public Album(int idx, int playCnt) {
            this.idx = idx;
            this.playCnt = playCnt;
        }

        @Override
        public int compareTo(Album album) {
            if(this.playCnt == album.playCnt) {
                return this.idx - album.idx;
            } else {
                return album.playCnt - this.playCnt;
            }
        }
    }

    public int[] solution(String[] genres, int[] plays) {
        int len = genres.length;
        Map<String, Integer> genreRank = new HashMap<>();
        Map<String, List<Album>> albumRank = new HashMap<>();

        // 데이터 초기화
        for (int i = 0; i < len; i++) {
            genreRank.put(genres[i], genreRank.getOrDefault(genres[i], 0) + plays[i]);
            albumRank.putIfAbsent(genres[i], new ArrayList<>());
            albumRank.get(genres[i]).add(new Album(i, plays[i]));
        }

        // 장르별 총 재생 횟수에 따라 장르 정렬
        List<String> sortedGenres = new ArrayList<>(genreRank.keySet());
        sortedGenres.sort((a, b) -> genreRank.get(b) - genreRank.get(a));

        List<Integer> ans = new ArrayList<>();

        // 각 장르 내에서 앨범 정렬 및 상위 2곡 선택
        for (String genre : sortedGenres) {
            List<Album> genreAlbum = albumRank.get(genre);
            Collections.sort(genreAlbum);
            for (int i = 0; i < Math.min(2, genreAlbum.size()); i++) {
                ans.add(genreAlbum.get(i).idx);
            }
        }

        // 결과를 배열로 반환
        return ans.stream().mapToInt(Integer::intValue).toArray();
    }
}

코드 설명

static class Album implements Comparable<Album> {
    int idx, playCnt;

    public Album(int idx, int playCnt) {
        this.idx = idx;
        this.playCnt = playCnt;
    }

    @Override
    public int compareTo(Album album) {
        if(this.playCnt == album.playCnt) {
            return this.idx - album.idx;
        } else {
            return album.playCnt - this.playCnt;
        }
    }
}

 

  • Album 클래스:
    • 각 앨범(노래)의 인덱스(idx)와 재생 횟수(playCnt)를 나타냅니다.
    • compareTo 메서드는 재생 횟수를 기준으로 내림차순 정렬하며, 재생 횟수가 같으면 인덱스 기준으로 오름차순 정렬합니다.
  • solution 메서드:
    • genreRank 해시맵: 각 장르별 총 재생 횟수를 저장합니다.
    • albumRank 해시맵: 각 장르별로 Album 객체 리스트를 저장합니다.
    • for 루프를 사용해 genreRank와 albumRank를 초기화합니다.
      • 각 노래를 해당 장르의 총 재생 횟수에 추가하고, Album 객체를 장르 리스트에 추가합니다.
    • genreRank의 키(장르)를 재생 횟수에 따라 내림차순으로 정렬하여 sortedGenres 리스트를 만듭니다.
      sortedGenres.sort((a, b) -> genreRank.get(b) - genreRank.get(a));
// 각 장르 내에서 앨범 정렬 및 상위 2곡 선택
for (String genre : sortedGenres) {
    List<Album> genreAlbum = albumRank.get(genre);
    Collections.sort(genreAlbum);
    for (int i = 0; i < Math.min(2, genreAlbum.size()); i++) {
        ans.add(genreAlbum.get(i).idx);
    }
}
  • 장르 내 노래 정렬 및 선택:
    • 각 장르의 노래 리스트(genreAlbum)를 Collections.sort를 통해 정렬합니다.
    • 상위 2개의 노래 인덱스를 결과 리스트 ans에 추가합니다.
  • int[] 타입으로 반환해야하니 ans 리스트를 배열로 변환해 반환합니다.
    return ans.stream().mapToInt(Integer::intValue).toArray();

 

제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers 알고리즘 고득점 Kit입니다.

포인트

확률에서 사용했던 조합을 이용하였습니다. 각 종류별로 nC1을 하고 모든 의상을 안입는 경우를 제외하여 경우의 수를 구하였습니다.

의상 소스코드

import java.util.*;

class Solution {
    
    public int solution(String[][] clothes) {
        int len = clothes.length;
        Map<String, Integer> matches = new HashMap<>();
        
        for(int i = 0; i < len; i++) {
            String item = clothes[i][0];
            String kind = clothes[i][1];
            matches.put(kind, matches.getOrDefault(kind, 0) + 1);
        }
        int answer = 1;
        
        for(String key: matches.keySet()) {
            answer *= matches.get(key) + 1;
        }

        return answer - 1;
        
    }
}

코드 설명

 

  • matches 해시맵: 각 옷의 종류별 개수를 저장합니다.
    • clothes[i][0]은 옷의 이름, clothes[i][1]은 옷의 종류입니다.
    • 해시맵에 옷의 종류를 키로, 해당 종류의 옷의 수를 값으로 저장합니다.
  • 모든 옷의 조합 계산:
    • answer는 초기값으로 1로 설정됩니다.
    • 각 옷의 종류마다 (해당 종류의 옷 수 + 1)를 answer에 곱해줍니다.
    • +1은 해당 종류의 옷을 입지 않는 경우를 포함하기 위해 사용됩니다.
  • 결과 반환:
    • answer - 1은 아무 옷도 입지 않는 경우를 제외한 옷의 조합 수를 반환합니다.

 

+ Recent posts