programmers.co.kr/learn/courses/30/lessons/42862

 

코딩테스트 연습 - 체육복

점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다. 다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다. 학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번

programmers.co.kr

전체 학생 수 int n, 체육복을 도둑맞은 학생 번호 int[] lost, 여벌 체육복을 가져온 학생 번호 int[] reserve 가 주어진다.

도둑맞은 학생들이 여벌을 가져온 학생들에게 체육복을 빌려 입었을 때 체육수업에 가장 많이 참여할 수 있는 학생의 수를 반환해야한다.

단, 여벌을 가져온 학생은 자신의 앞, 뒤 번호 학생에게만 빌려줄 수 있다. (ex : 4번은 3, 5번에게만 빌려줄 수 있다.)

(여벌을 가져온 학생이 체육복을 도난당했을 경우 1개만 도난당한 것으로 한다.)

 

 

 

풀이 .

import java.util.*;

class Solution {
    public int solution(int n, int[] lost, int[] reserve) {
        int answer = 0;
        
        // 일단 정렬
        List<Integer> lostList = new ArrayList<>();
        List<Integer> reserveList = new ArrayList<>();
        for (int num : lost) lostList.add(num);
        for (int num : reserve) reserveList.add(num);
        Collections.sort(lostList);
        Collections.sort(reserveList);

        // 여벌을 가져왔어도 도난당했다면 빌려줄 수 없다
        for (int i = 0; i < reserveList.size(); i++) {
            int num = reserveList.get(i);
            if (lostList.contains(num)) {
                lostList.remove((Integer) num);  // remove(Object o) 사용
                reserveList.remove((Integer) num);
                i--;  // 인덱스 당겨줘야 함
            }
        }

        // 작은 번호에게 우선적으로 빌려준다
        for (int num : reserveList) {
            if (lostList.contains(num - 1)) {
                lostList.remove((Integer)(num - 1));
                continue;
            }
            if (lostList.contains(num + 1)) {
                lostList.remove((Integer)(num + 1));
                continue;
            }
        }
        answer = n - lostList.size();
        return answer;
    }
}

 

여벌을 가져온 사람은 나보다 1 작은, 나보다 1 큰 사람에게 빌려줄 수 있다.

이때 무조건 나보다 작은 사람을 우선적으로 빌려주는 식으로 진행하면 된다.

 

반복을 돌리기 위해 정렬을 먼저 해주고 도난, 여벌 동시에 해당되는 놈들을 목록에서 제거해준다.

 

(주의)

ArrayList에는 remove(int index), remove(Object o) 두 가지 remove가 있다.

ArrayList<Integer> 에서는 remov(숫자)를 사용하면 무조건 remove(int index)가 호출된다.

(이것 때문에 OutOfBoundsException이 발생했다.)

 

remove(Object o)를 사용하고 싶다면

remove(Integer.valueOf(int)), remove(new Integer(int)) or remove((Integer) int) 세 가지 방법을 사용할 수 있다.

이유는 모르지만 마지막의 remove((Integer) int)가 가장 효율적이라고 한다.

'알고리즘 문제 > 프로그래머스' 카테고리의 다른 글

[PG] 큰 수 만들기 JAVA  (0) 2021.01.03
[PG] 조이스틱 JAVA  (0) 2021.01.02
[PG] 카펫 JAVA  (0) 2021.01.01
[PG] 소수 찾기 JAVA  (0) 2021.01.01
[PG] 모의고사 JAVA  (0) 2021.01.01

programmers.co.kr/learn/courses/30/lessons/42842

 

코딩테스트 연습 - 카펫

Leo는 카펫을 사러 갔다가 아래 그림과 같이 중앙에는 노란색으로 칠해져 있고 테두리 1줄은 갈색으로 칠해져 있는 격자 모양 카펫을 봤습니다. Leo는 집으로 돌아와서 아까 본 카펫의 노란색과

programmers.co.kr

노란색, 갈색으로 이뤄진 격자 모양의 카펫이 있다.

이 카펫의 노란색, 갈색 격자 개수가 주어질 때 카펫의 가로, 세로 크기를 배열에 담아 반환해야한다.

 

1. 카펫의 테두리는 모두 갈색 한 줄로 둘러싸여있고 나머지는 전부 노란색이다.

2. 가로 길이는 세로 길이와 같거다 더 길다.

3. 노란색 격자는 최소 1칸은 존재한다.

 

 

풀이 .

class Solution {
    public int[] solution(int brown, int yellow) {
        // b = 2w + 2h - 4
        //   -> w + h = (b + 4) / 2
        //   -> h < (b + 4) / 2
        // y = (w - 2) * (h - 2)

        int[] answer = new int[2];
        for(int h = 3; h < (brown + 4) / 2; h++) {  // y는 최소 1칸 -> h는 최소 3이상
            int w = (brown + 4) / 2 - h;
            if(yellow == (w - 2) * (h - 2)) {
                answer[0] = w;
                answer[1] = h;
                break;
            }
        }
        return answer;
    }
}

 

다른 블로그의 코드를 찾아보고 이해했다.

카펫의 형태를 유추하여 몇가지 식을 세울 수 있다.

가로는 세로보다 짧을 수 없다는 조건이 있기 때문에 h를 최소값부터 탐색하다 처음 나오는 w 값이 정답이 된다.

(w, h 값을 역전시키면 yellow에 대한 식은 맞지만 세로가 더 길어지므로 정답이 될 수 없음)

'알고리즘 문제 > 프로그래머스' 카테고리의 다른 글

[PG] 조이스틱 JAVA  (0) 2021.01.02
[PG] 체육복 JAVA  (0) 2021.01.02
[PG] 소수 찾기 JAVA  (0) 2021.01.01
[PG] 모의고사 JAVA  (0) 2021.01.01
[PG] 가장 큰 수 JAVA  (0) 2020.12.31

programmers.co.kr/learn/courses/30/lessons/42839

 

코딩테스트 연습 - 소수 찾기

한자리 숫자가 적힌 종이 조각이 흩어져있습니다. 흩어진 종이 조각을 붙여 소수를 몇 개 만들 수 있는지 알아내려 합니다. 각 종이 조각에 적힌 숫자가 적힌 문자열 numbers가 주어졌을 때, 종이

programmers.co.kr

숫자 문자열 String numbers가 주어진다.

numbers를 이루는 각 숫자들로 만들 수 있는 모든 숫자들의 조합에서 소수가 몇 개인지 반환해야한다.

단, 11과 011은 같은 것으로 치고 하나만 센다.

 

 

풀이 1. 

import java.util.*;

class Solution {
    ArrayList<Integer> pList = null;
    boolean[] check = null;
    int ans = 0;

    public boolean isPrime(int num) {
        if(num == 2 && !pList.contains(num)) {
            pList.add(num);
            return true;
        }

        if(num == 0 || num == 1 || num % 2 == 0 || pList.contains(num)) {
            return false;
        }

        for(int i = 3; i <= Math.sqrt(num); i+=2) {
            if(num % i == 0) {
                return false;
            }
        }

        pList.add(num);
        return true;
    }

    public void dfs(int dept, int deptNow, String comb, String numbers) {
        if(dept == deptNow) {  // 목표 자리수에 도달했다면 prime 검사 후 return
            int num = Integer.parseInt(comb);
            if(isPrime(num)) {
                this.ans++;
            }
            return;
        }

        // 백트래킹을 통해 모든 조합 구현
        for(int i = 0; i < numbers.length(); i++) {
            if(check[i] == false) {
                String temp = comb;  // 다시 돌아오기 위해 저장
                comb += String.valueOf(numbers.charAt(i));
                check[i] = true;

                dfs(dept, deptNow + 1, comb, numbers);

                // 백트래킹
                comb = temp;
                check[i] = false;
            }
        }
    }

    public int solution(String numbers) {
        int answer = 0;

        pList = new ArrayList<>();
        check = new boolean[numbers.length()];

        // 1~number.length() 모든 자리수의 조합을 구한다
        for(int i = 1; i <= numbers.length(); i++) {
            dfs(i, 0, "", numbers);
        }

        answer = this.ans;
        return answer;
    }
}

 

주어진 문자열로 만들어낼 수 있는 모든 조합을 구한다.

조합이 하나 완성될 때마다 소수에 해당되는지 검사한다. 

 

1. 소수 검사

기본적으로 나머지 연산을 사용한다. 이때 1 ~ num을 모두 나머지 연산 하는 것은 비효율적이다.

0, 1, 2에 대한 검사를 마친 후 3~num^(1/2) 까지만 검사하면 된다. 왜인지는 모른다.

 

2. 조합 생성

백트래킹을 사용하여 구할 수 있는 모든 조합을 구했다.

조금 걸리는 부분이 있는데.. 이렇게 짜면 number = "444333" 이렇게 같은 숫자가 반복되는 String이 주어졌을 때 똑같은 조합을 여러번 구하는 낭비가 일어난다.

444333이란 조합은 총 36개가 나오게 된다. (3! * 3!)

 

이 문제에서는 numbers의 최대길이는 7이고 7! = 5040 개의 초합이 최대이기 때문에 시간초과는 나지 않지만 n이 조금만 더 커지면 굉장히 불안해진다.. 

 

어떻게 해결할 지 생각해 봐야겠다.

 

 

 

풀이 2.

// 백트래킹을 통해 모든 조합 구현
ArrayList<Character> used = new ArrayList<>();
for(int i = 0; i < numbers.length(); i++) {
	// 다음 숫자가 이번 자리수에서 쓰인 적 있는지 검사
	if(used.contains(numbers.charAt(i))) continue;

    if(check[i] == false) {
    	String temp = comb;  // 다시 돌아오기 위해 저장
        comb += String.valueOf(numbers.charAt(i));
        used.add(numbers.charAt(i));  // 이번 자리수에서 사용된 숫자 표시
        check[i] = true;

        dfs(dept, deptNow + 1, comb, numbers);

        // 백트래킹
        comb = temp;
        check[i] = false;
	}
}

 

위 코드에서 dfs()에 코드 몇 줄을 추가하여 효율성 문제를 해결했다. (수정 전에도 시간초과 안 뜨긴 했지만)

 

각각의 dfs()는 deptNow번째 자리수에 새로운 숫자를 집어넣고 deptNow+1 번째 자리수로 재귀한다.

 

이때 각 dfs()마다 ArrayList를 두어 해당 자리수에서 한 번 사용된 숫자는 다시 사용될 수 없도록 하여 중복조합의 경우를 모두 제거했다.

 

n이 커질 경우 이를 통해 시간을 대폭 감소시킬 수 있다.

 

반복해서 생성되는 ArrayList 때문에 메모리 초과가 나지 않을까 싶긴 하지만.. 그래도 이 방법이 더 나은 듯 싶다.

 

'알고리즘 문제 > 프로그래머스' 카테고리의 다른 글

[PG] 체육복 JAVA  (0) 2021.01.02
[PG] 카펫 JAVA  (0) 2021.01.01
[PG] 모의고사 JAVA  (0) 2021.01.01
[PG] 가장 큰 수 JAVA  (0) 2020.12.31
[PG] K 번째 수  (0) 2020.12.31

programmers.co.kr/learn/courses/30/lessons/42840

 

코딩테스트 연습 - 모의고사

수포자는 수학을 포기한 사람의 준말입니다. 수포자 삼인방은 모의고사에 수학 문제를 전부 찍으려 합니다. 수포자는 1번 문제부터 마지막 문제까지 다음과 같이 찍습니다. 1번 수포자가 찍는

programmers.co.kr

세 명의 수포자가 모의고사를 치른다. 각 수포자들은 각각 다른 패턴으로 정답을 찍는다.

모의고사의 정답지 int[] answers가 주어질 때, 정답을 가장 많이 맞춘 수포자를 배열에 담아 반환해야한다.

단, 공동 1위가 나온다면 수포자 번호의 오름차순으로 담아 반환한다.

 

 

풀이 .

import java.util.*;

class Solution {
    public List<Integer> solution(int[] answers) {
        ArrayList<Integer> answer = new ArrayList<>();

        int[] a = {1, 2, 3, 4, 5};
        int[] b = {2, 1, 2, 3, 2, 4, 2, 5};
        int[] c = {3, 3, 1, 1, 2, 2, 4, 4, 5, 5};

        int[] score = new int[3];  // 각 수포자들의 점수 체크
        for(int i = 0; i < answers.length; i++) {
            if(a[i % a.length] == answers[i]) score[0]++;
            if(b[i % b.length] == answers[i]) score[1]++;
            if(c[i % c.length] == answers[i]) score[2]++;
        }

        int max = Math.max(score[0], Math.max(score[1], score[2]));
        if(max == score[0]) answer.add(1);
        if(max == score[1]) answer.add(2);
        if(max == score[2]) answer.add(3);
        return answer;
    }
}

 

나머지 연산을 잘 써야하는 문제.

n % m 의 결과는 0 ~ m-1만 나온다는 것을 기억하자.

i % a.length 의 결과는 0 ~ a.length-1을 반복한다. 따라서 a[]의 인덱스 범위 내에서만 반복을 돌게 된다.

'알고리즘 문제 > 프로그래머스' 카테고리의 다른 글

[PG] 카펫 JAVA  (0) 2021.01.01
[PG] 소수 찾기 JAVA  (0) 2021.01.01
[PG] 가장 큰 수 JAVA  (0) 2020.12.31
[PG] K 번째 수  (0) 2020.12.31
[PG] 이중 우선순위 큐 JAVA  (0) 2020.12.31

1. SRP : 단일 책임 원칙 (Single responsibility principle)

- 한 클래스는 하나의 책임만을 가져야 한다.

 

2. OCP : 개방/폐쇄 원칙 (Open/closed principle)

- 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.

- 다형성을 사용해도 OCP를 위배하고 마는데 어떻게? -> AppConfig로 해결했으나 비효율적 -> 이를 해결하기 위해 스프링 등장

 

3. LSP : 리스코프 치환 원칙 (Liskov substitution principle)

- 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.

- 다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것, 다형성을 지원하기 위 한 원칙, 인터페이스를 구현한 구현체는 믿고 사용하려면, 이 원칙이 필요하다.

- 프로그램의 정확성?

-> ex : 자동차 인터페이스의 goForward() 는 앞으로 나아가는 기능을 해야한다. 나아가는 방식이 다르더라도 반드시 '앞으로' 나아가야 한다

 

4. ISP : 인터페이스 분리 원칙 (Interface segregation principle)

- 범용 인터페이스 하나보다는 여러 개의 인터페이스로 분리하는 것이 낫다.

- ex : 자동차 인터페이스 하나보다는 운전 인터페이스, 정비 인터페이스 둘로 나누는 것이 낫다.

 

5. DIP : 의존관계 역전 원칙 (Dependency inversion principle)

- 프로그래머는 “추상화에 의존해야지, 구체화에 의존하면 안된다.” 의존성 주입은 이 원칙 을 따르는 방법 중 하나다.

- 구현 클래스에 의존하지 말고 인터페이스에 의존하도록 해야한다는 말

- ex : 아반떼에 의존하는 것이 아니라 자동차에 의존해야한다.

- OCP와 마찬가지로 다형성을 사용해도 DIP를 위배하게 된다. -> AppConfig로 해결했으나 비효율적 -> 스프링으로 해결

 

 

 

출처 : www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard

programmers.co.kr/learn/courses/30/lessons/42746

 

코딩테스트 연습 - 가장 큰 수

0 또는 양의 정수가 주어졌을 때, 정수를 이어 붙여 만들 수 있는 가장 큰 수를 알아내 주세요. 예를 들어, 주어진 정수가 [6, 10, 2]라면 [6102, 6210, 1062, 1026, 2610, 2106]를 만들 수 있고, 이중 가장 큰

programmers.co.kr

숫자들의 배열 int[] numbers가 주어진다.

숫자들을 이어붙였을때(String의 합처럼) 만들 수 있는 가장 큰 수를 반환해야한다.

 

 

풀이 .

import java.util.*;

class Solution {
    public String solution(int[] numbers) {
        String[] arr = new String[numbers.length];
        for(int i = 0; i < arr.length; i++) {
            arr[i] = String.valueOf(numbers[i]);
        }

        Arrays.sort(arr, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return (o2 + o1).compareTo(o1 + o2);  // 주의, String 끼리는 부등호 연산 사용할 수 없음
            }
        });

        // 예외처리, 맨 앞칸이 0이면 00~0의 형태. 즉, 0
        if(arr[0].equals("0")) {
            return "0";
        }

        StringBuilder sb = new StringBuilder();
        for(String str : arr) {
            sb.append(str);
        }
        return sb.toString();
    }
}

String에 대한 Comparator를 새로 만들어서 문제를 해결했다.

 

주의할 점은 compare()에서 반드시 compareTo()를 사용해야 한다는 점이다.

return (o1 + o2) < (o2 + o1) ? 1 : -1 을 사용하려고 했으나 String 끼리는 부등호 연산을 사용할 수 없었다.

 

비슷한 경우로 int 끼리는 compareTo()를 사용할 수 없다. 만약 사용하고 싶다면 Integer를 사용해야한다.

 

결과 출력에 굳이 StringBuilder를 사용하지 않고 + 연산으로 처리해도 된다.

하지만 시간차가 심하기 때문에 웬만해선 StringBuilder를 사용하는 것이 좋다.

'알고리즘 문제 > 프로그래머스' 카테고리의 다른 글

[PG] 소수 찾기 JAVA  (0) 2021.01.01
[PG] 모의고사 JAVA  (0) 2021.01.01
[PG] K 번째 수  (0) 2020.12.31
[PG] 이중 우선순위 큐 JAVA  (0) 2020.12.31
[PG] 디스크 컨트롤러 JAVA  (0) 2020.12.31

programmers.co.kr/learn/courses/30/lessons/42748

 

코딩테스트 연습 - K번째수

[1, 5, 2, 6, 3, 7, 4] [[2, 5, 3], [4, 4, 1], [1, 7, 3]] [5, 6, 3]

programmers.co.kr

int[] array, int[][] commands가 주어진다.

commands 의 각 행에는 명령을 위한 3개의 int가 들어있다.

 

[begin, end, K]

명령에 대한 답은 array에서 [begin번째 ~ end번째]만큼을 잘라내고 그 중 K번째로 큰 값이다.

(단, begin, end는 인덱스를 나타내지 않는다. 1부터 시작하는 순서를 나타낼 뿐이다.)

 

commands 의 각 명령들에 대한 답을 담은 배열을 반환해야한다.

 

 

풀이 . 

import java.util.*;

class Solution {
    public int[] solution(int[] array, int[][] commands) {
        int[] answer = new int[commands.length];

        int ansIdx = 0;
        for(int[] command : commands) {
            int begin = command[0];
            int end = command[1];
            int k = command[2];

            int[] temp = new int[end - begin + 1];
            int idx = 0;
            for(int i = begin - 1; i <= end - 1; i++) {  // 인덱스와 순서 혼동 주의
                temp[idx++] = array[i];
            }
            Arrays.sort(temp);
            answer[ansIdx++] = temp[k - 1];
        }

        return answer;
    }
}

 문제에서 시키는 그대로 따라하면 된다.

'알고리즘 문제 > 프로그래머스' 카테고리의 다른 글

[PG] 모의고사 JAVA  (0) 2021.01.01
[PG] 가장 큰 수 JAVA  (0) 2020.12.31
[PG] 이중 우선순위 큐 JAVA  (0) 2020.12.31
[PG] 디스크 컨트롤러 JAVA  (0) 2020.12.31
[PG] 더 맵게 JAVA  (0) 2020.12.31

programmers.co.kr/learn/courses/30/lessons/42628

 

코딩테스트 연습 - 이중우선순위큐

 

programmers.co.kr

수행할 명령어들이 담긴 배열 String[] operations가 주어진다.

주어진 명령을 모두 수행한 뒤 남아있는 값 중 최대값, 최소값을 담은 배열을 반환해야한다.

단, 남아있는 값이 하나도 없을 경우 최대값, 최소값은 0으로 한다.

 

명령

I 숫자 : 숫자 입력

D 1 : 입력된 숫자 중 최대값 삭제

D -1 : 입력된 숫자 중 최소값 삭제

 

 

풀이 .

import java.util.*;

class Solution {
    public int[] solution(String[] operations) {
        int[] answer = new int[2];

        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());

        for(String operation : operations) {
            StringTokenizer st = new StringTokenizer(operation);
            switch(st.nextToken()) {  // work
                case "I" :  // input
                    int input = Integer.parseInt(st.nextToken());
                    minHeap.add(input);
                    maxHeap.add(input);
                    break;
                case "D" :  // delete
                    if(minHeap.isEmpty()) {
                        continue;
                    }
                    if(st.nextToken().equals("1")) {  // 최대값 삭제
                        int max = maxHeap.poll();
                        minHeap.remove(max);
                    }else {  // 최소값 삭제
                        int min = minHeap.poll();
                        maxHeap.remove(min);
                    }
                    break;
            }
        }

        if(minHeap.isEmpty()) {  // 예외처리
            answer[0] = 0;
            answer[0] = 0;
        }else {
            answer[0] = maxHeap.peek();
            answer[1] = minHeap.peek();
        }
        return answer;
    }
}

최대값과 최소값을 동시에 다루기 위해 두 개의 PriorityQueue를 사용하였다.

각 명령을 두 pq에 함께 수행한 뒤 최대, 최소를 반환하면 된다.

pq.remove(Object o)가 가능한 것을 알게 되었다.

 

백준에선 자주 쓰이지만 프로그래머스에서 StringTokenizer 를 사용한 것은 처음이었다.

(StringTokenizer 를 사용해 " "으로 split한 문자열들을 얻을 수 있다. 다른 것으로 split 하고 싶다면 두 번째 매개변수로 줄 수 있다.)

'알고리즘 문제 > 프로그래머스' 카테고리의 다른 글

[PG] 가장 큰 수 JAVA  (0) 2020.12.31
[PG] K 번째 수  (0) 2020.12.31
[PG] 디스크 컨트롤러 JAVA  (0) 2020.12.31
[PG] 더 맵게 JAVA  (0) 2020.12.31
[PG] 프린터 JAVA  (0) 2020.12.30

+ Recent posts