알고리즘/프로그래머스

2023.02.09(목) - 프로그래머스 고득점 Kit 스터디

맏리믓 2023. 2. 9. 05:35

- 체육복

// 제출 답안
package suhyeongLee.Gready.Workout_clothes;

import java.util.Arrays;

class Solution {
    public int solution(int n, int[] lost, int[] reserve) {
    	// 처음 운동 가능 한 사람은 전체 인원에서 잃어버린 사람을 뺀 수
        int answer = n - lost.length;
        Arrays.sort(lost); // 실제 정답 데이터에 sort 안된 데이터가 존재
        Arrays.sort(reserve);
        
        // 잃어 버린 사람과 여분이 있는 사람이 같을 경우 하나만 가지고 온 사람과 동일함
        // 해당 자리에 사람 번호 대신 음수를 넣어 표기
        for(int i = 0; i < lost.length; i++){
            for(int j = 0; j < reserve.length; j++){
                if(lost[i] == reserve[j]){
                    lost[i] = -1;
                    reserve[j] = -10;
                    answer += 1;
                }
            }
        }

		//빌려 줄 수 있는 사람 양 옆에 분실 한 사람이 있을 경우 위와 같은 방법으로 
        //lost 와 reserve 에 음수 값을 넣어 표기
        for(int i = 0; i < lost.length; i++){
            for(int j = 0; j < reserve.length; j++){
                if((reserve[j] == lost[i]-1) || (reserve[j] == lost[i]+1)){
                    lost[i] = -1;
                    reserve[j] = -10;
                    
                    //빌려주면서 운동 가능 한 사람이 한명 증가
                    answer += 1;
                }
            }
        }
        return answer;
    }
}
//다른 코드
// 모든 인원의 체육복 수를 기록 해 둔 people 배열을 이용해 문제 해결
class Solution {
    public int solution(int n, int[] lost, int[] reserve) {
        int[] people = new int[n];
        int answer = n;

		//잃어버린 사람은 "-1"
        for (int l : lost) 
            people[l-1]--;
            
        //여분이 있는 사람은 "1"
        for (int r : reserve) 
            people[r-1]++;

		//옆사람에게 빌려준 경우 여분있는 번호는 -1, 부족한 번호는 +1 을 함
        for (int i = 0; i < people.length; i++) {
            if(people[i] == -1) {
                if(i-1>=0 && people[i-1] == 1) {
                    people[i]++;
                    people[i-1]--;
                }else if(i+1< people.length && people[i+1] == 1) {
                    people[i]++;
                    people[i+1]--;
                }else 
                    answer--;
            }
        }
        return answer;
    }
}

 

- 조이스틱

    풀이 방법

      - 각 자리 글자는

           A -> B 로 올라갈때는 ASCII(목표) - ASCII(A)

           A -> Z 로 반대로 갈 때는 ASCII(Z) - ASCII(목표) + 1

 

      - 좌우로 이동 시 A 는 건드릴 필요가 없으므로 A 를 최대한 피해 가는게 좋다.

      - 이동 방법은 다음 예시에서

           left -> mid -> right 로 가는 방법(K -> D -> A -> A -> F -> I -> A -> A -> E -> A -> K -> L -> S)

           left -> right 로 가는 방법(K -> D -> K -> S -> L -> K -> A -> E -> A -> A -> I -> F)

           right -> left 로 가는 방법(S -> L -> K -> A -> E -> A -> A -> I -> F -> I -> A -> A -> .....-> L -> S -> K -> D)

      - 글자를 바꾸기 위해 소모되는 횟수는 절대적이기 때문에 이동 방법만 최소가 되도록 해주면 해결 가능하다.

 

//제출 답안
package suhyeongLee.Gready.Joystick;

class Solution {
    public int solution(String name) {
        int answer = 0;
        
        //가장 일반적인 이동은 좌측에서 우측으로 이동하면서 바꾸는 방법
        int move = name.length()-1;
        
        //글자를 처음부터 확인
        for(int i = 0; i < name.length(); i++){
        	// 해당 글자를 만들기 위해 소모되는 조작 횟수
            int Goal_ascii = (int)name.toCharArray()[i];
            answer += Math.min(Goal_ascii-(int)'A', (int)'Z'-Goal_ascii+1);
            
            //끝에 다다르지 않았으면서 해당 글자가 A 면 돌아갈지 계속 갈지 선택 해야 함
            if(i<name.length()-1 && name.toCharArray()[i+1] == 'A'){
                int j = i + 1;
                while(j<name.length() && name.toCharArray()[j] == 'A'){
                    j++;
                }
                move = Math.min(move, Math.min((2*i + name.length()-j), (i + 2*(name.length()-j))) );
            }
        }
        answer += move;
        return answer;
    }
}
//다른 예시
class Solution {
public int solution(String name){
        int answer = 0, n = name.length(),
                leftOrRight = name.length() - 1;
        for(int i = 0; i < n; i++){
            int next = i + 1;
            char target = name.charAt(i);
            
            //diff : min 을 사용하여 비교하지 않고 기준을 잡아 구분
            if(target <= 'N') answer += target - 'A';
            else answer += 'Z' - target + 1;
            while(next < n && name.charAt(next) == 'A') next++;
            
            //문제 개편전이라 right -> left 에 해당하는 코드 부재
            leftOrRight = Math.min(leftOrRight, i + n - next + Math.min(i, n - next));
        }
        answer += leftOrRight;
        return answer;
    }
}

 

- 큰 수 만들기

    풀이 방법

      - 최대한 큰 수가 되기 위해선 앞자리 수가 최대한 커야 한다.

      - 그림과 같이 stack 을 이용하여 앞에서 부터 하나씩 stack 에 넣어줌

      - 그 후 stack 의 top 이 남은 수의 가장 앞의 수보다 클때까지 pop 을 해줌  

      - 이 작업을 pop 횟수가 k 번 될때까지 반복

//제출 답안
package suhyeongLee.Gready.Big_number;

import java.util.ArrayDeque;
import java.util.Deque;

class Solution {
    public String solution(String number, int k) {
        String answer = "";
        char[] num_arr = number.toCharArray();
        
        // 마지막에 수를 뽑아내기 편하게 하기 위해서 deque 를 사용함
        Deque<Integer> deque = new ArrayDeque<>();
        int index = 0, i = 0;
        while(i < num_arr.length){
            int tmp = Integer.parseInt(Character.toString(num_arr[i]));
            if(deque.size()!=0 && tmp>deque.peek() && index<k){
                deque.pop();
                index += 1;
            } else {
                deque.push(tmp);
                i += 1;
            }
        }

        while(deque.size() > num_arr.length-k){
            deque.pop();
        }

        int size = deque.size();
        for(int j = 0; j < size; j++){
            answer += deque.pollLast().toString();
        }

        return answer;
    }
}

 

// 다른 예제
//모든 수를 훑어 가면서 i 번째 수가 i+1 번째 수보다 작으면 그 수를 제거하는 방식
//맨처음 떠올린 방법이지만 java 에서 String 은 원하는 위치의 글자를 제거할 수 없어서 포기
//StringBuilder 를 사용하면 해결 가능
class Solution {
    public String solution(String number, int k) {
        StringBuilder sb = new StringBuilder(number);
        for (int i = 0; i+1 < sb.length() && k>0; i++) {
            if(sb.charAt(i) < sb.charAt(i+1)) {
                sb.deleteCharAt(i);
                i=-1;
                k--;
            }
        }
        if(k!=0)
            sb.delete(sb.length()-k, sb.length());
        return sb.toString();
    }
}

 

- 구명 보트

    풀이 방법

      - 무게 순으로 정렬 후 가장 무거운 사람과 가장 가벼운 사람을 조합하여 구출

      - 조합 시 둘다 탑승이 불가한 경우 무거운 사람만 먼저 탑승

      - 탑승 후 가벼운 사람과 무거운 사람 index 를 변경하며 문제 해결

// 제출 답안
package suhyeongLee.Gready.Lifeboat;

import java.util.Arrays;

class Solution {
    public int solution(int[] people, int limit) {
        int answer = 0;
        Arrays.sort(people);
        int min_index = 0;
        int max_index = people.length-1;

        while(min_index <= max_index){
            if(people[min_index]+people[max_index] > limit){
                max_index -= 1;
            } else {
                min_index += 1;
                max_index -= 1;
            }
            answer += 1;
        }
        return answer;
    }
}
// 다른 예시
// 알고리즘은 동일 하지만 더 짧게 작성
import java.util.Arrays;

class Solution {
    public int solution(int[] people, int limit) {
        Arrays.sort(people);
        int i = 0, j = people.length - 1;
        for (; i < j; --j) {
            if (people[i] + people[j] <= limit)
                ++i;
        }
        return people.length - i;
    }
}

 

- 섬 연결하기

    union-find 알고리즘

      - 합집합을 만들고 같은 집합에 속해 있는지 찾아 낼 수 있는 알고리즘

      - 우선 각 원소는 자기 자신을 부모 값으로 가진다.

      - 만약 "a" 와 "b" 를 union 할 경우 두 원소가 연결 되며 같은 부모를 가지게 된다

      - 만약 따라서 b 의 부모가 a 가 된다.

      - 같은 방법으로 "c" 와 "b"를 union 하게 되면 "c" 의 부모는 "b"가 된다.

      - 이때 "c"의 부모인 "b" 의 부모가 "a" 이기 때문에 "c"의 부모를 "a" 로 수정한다.

      - 여기 까지 진행하고 union 을 마친다고 가정하면 부모가 같은 "a, b, c" 는 같은 집합안에 속하지만 부모가 다른 "d"는 집합이 다르다는걸 알 수 있다.

// find 함수
    public static int find(int a) {
    	if(parent[a] == a)
    		return a;
    	else
    		return parent[a] = find(parent[a]);
    }
    
// union 함수
    public static void union(int a, int b) {
    	a = find(a);
    	b = find(b);
    	if(a != b)
    		parent[b] = a;
    }

 

    풀이 방법

      - union-find 알고리즘을 사용

      - 입력 데이터인 costs 를 비용 순으로 오름차순 정렬 

            이를 통해 costs 를 앞에서 부터 union 해도 비용 발생이 최소화 가늘

      - 다음 그림과 같이 시작노드(costs[i][0]) 와 도착 노드(costs[i][1]) 의 부모가 같은지 판별(union 에 해당)

            1. 만약 같다면 해당 node 는 연결 되었기 때문에 그냥 넘어감(continue;)

            2. 같지 않다면 도착 노드의 부모를 시작 노드의 부모로 변경해 줌

            3. 2번의 경우에만 새로 다리를 연결 했기 때문에 cost 를 증가

            * 이 후에는 모든 원소의 부모가 동일 하기 때문에 별다른 조작을 하지 않음!

// 제출 답안
package suhyeongLee.Gready.Island_Connection;

import java.util.Arrays;

class Solution {
    int[] parent;
    public int find(int a){
        if(parent[a] == a){
            return a;
        } else {
            return parent[a] = find(parent[a]);
        }
    }

    public int solution(int n, int[][] costs) {
        int answer = 0;
        parent = new int[n];
        for(int i = 0; i < parent.length; i++){
            parent[i] = i;
        }

        Arrays.sort(costs, (int[] c1, int[] c2) -> c1[2]-c2[2]);

        for (int i = 0; i < costs.length; i++){
            int from_node = costs[i][0];
            int to_node = costs[i][1];
            int cost = costs[i][2];

            int from_parent = find(from_node);
            int to_parent = find(to_node);

            if(from_parent != to_parent){
                parent[to_parent] = from_parent;
            } else {
                continue;
            }

            answer += cost;
        }
        return answer;
    }
}

 

- 단속 카메라

    풀이 방법

        - 입력 데이터 중 routes 배열을 자동차가 나가는 지점(routes[1]) 을 기준으로 오름차순

            * 카메라를 최대한 적게 두기 위해선 자동차 이동 지점이 최대한 겹치는 위치에 설치

            * 위 조건을 만족 하기 위해선 한 자동차의 이동 지점 가운데가 아닌 최대한 시작 점이나 끝점에 설치 하는것이 유리

            * 정렬을 후 나가는 지점 을 기반으로 카메라를 설치 한다면 위 조건 만족 가능

 

        - 다음 그림 처럼 순차 적으로 routes 배열을 탐색 하며 카메라를 설치

            * ex) 1번 차량이 나가는 지점인 -15번에 카메라를 설치 하게 되면 2번 카메라도 감시 가능하기 때문에 2번 차량이 나가는 지점인 -13 에는 카메라를 설치 하지 x

            * ex) 같은 방법으로 3번 차량이 나가는 지점인 -5번 위치에도 카메라를 설치 하게 되면 4번 카메라도 감시가 가능 하기 때문에 -3 에는 카메라 설치 x 

            * 따라서 카메라 2개

//제출 답안
// 처음엔 반대로 시작지점을 기준으로 정렬 한 후 나가는 지점에 카메라를 설치 하는 방법을
// 생각 했는데
// [[-200, -100], [-190, -180], [-170, -160], ...] 처럼 시작 지점이 가장 작은 차량이 
// 나머지를 품는고 있는 구조에서 해결 불가능
package suhyeongLee.Gready.Intermittent_camera;

import java.util.Arrays;

class Solution {
    public int solution(int[][] routes) {
        int answer = 0;

        Arrays.sort(routes, (int[]c1, int[]c2) -> c1[1]-c2[1]);
        int cam_pos = -30000;
        for(int i = 0; i < routes.length; i++){
            if(cam_pos >= routes[i][0]) continue;
            cam_pos = routes[i][1];
            answer += 1;
        }

        return answer;
    }
}