문제 설명

정수 배열 numbers가 주어집니다. numbers에서 서로 다른 인덱스에 있는 두 개의 수를 뽑아 더해서 만들 수 있는 모든 수를 배열에 오름차순으로 담아 return 하도록 solution 함수를 완성해주세요.

 

 

제한사항

numbers의 길이는 2 이상 100 이하입니다.

numbers의 모든 수는 0 이상 100 이하입니다.

 

입출력 예

[2,1,3,4,1]

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

[5,0,2,7]

[2,5,7,9,12]


풀이

import java.util.TreeSet;

class Solution {
    public int[] solution(int[] numbers) {
    	//트리셋 특징인 오름차순으로 삽입정렬되는 방식을 이용,
        TreeSet<Integer> set = new TreeSet<>();
        
        for(int i = 0; i < numbers.length-1; i++){
            for(int j = i+1; j < numbers.length; j++){
                set.add(numbers[i] + numbers[j]);
            }
        }
        
        //자료형 변환 방법 1 (성능)
        int[] answer = new int[set.size()];
        
        int idx = 0;
        for(Integer i : set){
            answer[idx] = i.intValue();
            idx++;
        }
        
        //자료형 변환 방법 2 (가독성)
        //answer = set.stream().mapToInt(Integer::intValue).toArray();
        
        return answer;
    }
}

 

'알고리즘문제 > Java' 카테고리의 다른 글

2019 카카오 개발자 겨울 인턴십 - 튜플  (0) 2020.08.12
2019 카카오블라인드 : 실패율  (0) 2020.08.07
타일 장식물  (0) 2019.12.06
2018카카오블라인드 : 방금그곡  (0) 2019.12.05
2 * n 타일링  (0) 2019.12.05

문제출처 : https://programmers.co.kr/learn/courses/30/lessons/64065

 

코딩테스트 연습 - 튜플

"{{2},{2,1},{2,1,3},{2,1,3,4}}" [2, 1, 3, 4] "{{1,2,3},{2,1},{1,2,4,3},{2}}" [2, 1, 3, 4] "{{4,2,3},{3},{2,3,4,1},{2,3}}" [3, 2, 4, 1]

programmers.co.kr

 

[문제설명]

셀수있는 수량의 순서있는 열거 또는 어떤 순서를 따르는 요소들의 모음을 튜플(tuple)이라고 합니다. n개의 요소를 가진 튜플을 n-튜플(n-tuple)이라고 하며, 다음과 같이 표현할 수 있습니다.

  • (a1, a2, a3, ..., an)

튜플은 다음과 같은 성질을 가지고 있습니다.

  1. 중복된 원소가 있을 수 있습니다. ex : (2, 3, 1, 2)

  2. 원소에 정해진 순서가 있으며, 원소의 순서가 다르면 서로 다른 튜플입니다. ex : (1, 2, 3) ≠ (1, 3, 2)

  3. 튜플의 원소 개수는 유한합니다.

원소의 개수가 n개이고, 중복되는 원소가 없는 튜플 (a1, a2, a3, ..., an)이 주어질 때(단, a1, a2, ..., an은 자연수), 이는 다음과 같이 집합 기호 '{', '}'를 이용해 표현할 수 있습니다.

  • {{a1}, {a1, a2}, {a1, a2, a3}, {a1, a2, a3, a4}, ... {a1, a2, a3, a4, ..., an}}

예를 들어 튜플이 (2, 1, 3, 4)인 경우 이는

  • {{2}, {2, 1}, {2, 1, 3}, {2, 1, 3, 4}}

와 같이 표현할 수 있습니다. 이때, 집합은 원소의 순서가 바뀌어도 상관없으므로

  • {{2}, {2, 1}, {2, 1, 3}, {2, 1, 3, 4}}

  • {{2, 1, 3, 4}, {2}, {2, 1, 3}, {2, 1}}

  • {{1, 2, 3}, {2, 1}, {1, 2, 4, 3}, {2}}

는 모두 같은 튜플 (2, 1, 3, 4)를 나타냅니다.

특정 튜플을 표현하는 집합이 담긴 문자열 s가 매개변수로 주어질 때, s가 표현하는 튜플을 배열에 담아 return 하도록 solution 함수를 완성해주세요.

 

 

[제한사항]

 

  • s의 길이는 5 이상 1,000,000 이하입니다.

  • s는 숫자와 '{', '}', ',' 로만 이루어져 있습니다.

  • 숫자가 0으로 시작하는 경우는 없습니다.

  • s는 항상 중복되는 원소가 없는 튜플을 올바르게 표현하고 있습니다.

  • s가 표현하는 튜플의 원소는 1 이상 100,000 이하인 자연수입니다.

  • return 하는 배열의 길이가 1 이상 500 이하인 경우만 입력으로 주어집니다.

 

[코드]

import java.util.*;

class Solution {
    public int[] solution(String s) {
    
        String[] sArr = s.split("\\},\\{");
        sArr[0] = sArr[0].substring(2, sArr[0].length());
        sArr[sArr.length-1] = sArr[sArr.length-1].substring(0, sArr[sArr.length-1].length()-2);
        
        Arrays.sort(sArr, (s1, s2) -> s1.length() - s2.length());
        
        int[] answer = new int[sArr.length];
        int index = 0;
        
        Set<String> hashSet = new HashSet<>();
        for(String str : sArr){
            
            String strArr[] = str.split(",");
            for(String ansValue : strArr){                
                if(hashSet.add(ansValue)){
                    answer[index] = Integer.parseInt(ansValue);
                    index++;
                }            
            }            
        }
        
        return answer;
    }
}

 

 

[해석]

2차원 배열 형태의 문자열을 파싱.

- "},{"을 기준으로 문자열 배열을 만든뒤{String.split()} 처음과 끝값의 "{{", "}}"을 제거(String.substring())

 

원소의 크기(문자열의 길이)로 정렬

- Arrays.sort()에 길이로 비교하는 compare 메서드 오버라이드

 

순서대로 집어넣으며 중복제거

- sArr에 각 줄마다 값을 가져와 해쉬셋을 이용해 입력이 되면(중복되지 않으면) answer배열에 입력

 

answer 리턴

 

 

문제출처 : https://programmers.co.kr/learn/courses/30/lessons/42889

 

코딩테스트 연습 - 실패율

실패율 슈퍼 게임 개발자 오렐리는 큰 고민에 빠졌다. 그녀가 만든 프랜즈 오천성이 대성공을 거뒀지만, 요즘 신규 사용자의 수가 급감한 것이다. 원인은 신규 사용자와 기존 사용자 사이에 스��

programmers.co.kr

 

문제 설명

실패율

슈퍼 게임 개발자 오렐리는 큰 고민에 빠졌다. 그녀가 만든 프랜즈 오천성이 대성공을 거뒀지만, 요즘 신규 사용자의 수가 급감한 것이다. 원인은 신규 사용자와 기존 사용자 사이에 스테이지 차이가 너무 큰 것이 문제였다.

이 문제를 어떻게 할까 고민 한 그녀는 동적으로 게임 시간을 늘려서 난이도를 조절하기로 했다. 역시 슈퍼 개발자라 대부분의 로직은 쉽게 구현했지만, 실패율을 구하는 부분에서 위기에 빠지고 말았다. 오렐리를 위해 실패율을 구하는 코드를 완성하라.

  • 실패율은 다음과 같이 정의한다.

    • 스테이지에 도달했으나 아직 클리어하지 못한 플레이어의 수 / 스테이지에 도달한 플레이어 수

전체 스테이지의 개수 N, 게임을 이용하는 사용자가 현재 멈춰있는 스테이지의 번호가 담긴 배열 stages가 매개변수로 주어질 때, 실패율이 높은 스테이지부터 내림차순으로 스테이지의 번호가 담겨있는 배열을 return 하도록 solution 함수를 완성하라.

 

 

제한사항
  • 스테이지의 개수 N은 1 이상 500 이하의 자연수이다.

  • stages의 길이는 1 이상 200,000 이하이다.

  • stages에는 1 이상 N + 1 이하의 자연수가 담겨있다.

    • 각 자연수는 사용자가 현재 도전 중인 스테이지의 번호를 나타낸다.

    • 단, N + 1 은 마지막 스테이지(N 번째 스테이지) 까지 클리어 한 사용자를 나타낸다.

  • 만약 실패율이 같은 스테이지가 있다면 작은 번호의 스테이지가 먼저 오도록 하면 된다.

  • 스테이지에 도달한 유저가 없는 경우 해당 스테이지의 실패율은 0 으로 정의한다.

분석

말로 설명하면 아주 간단하다

1. 1~N사이의 각 스테이지에 유저들이 얼마나 도달했는지?

2. 도달한 데이터들을 실패자 수/도전자 수 순으로 정렬한다.

3. answer 배열에 담아 반환하면 끝.

 

 

해결

기본

public int[] solution(int N, int[] stages) {
        int people = stages.length;

        Arrays.sort(stages);

        Map<Integer, Double> hashMap = new HashMap<>();
        ...

전체 플레이어 수를 담은 people 변수를 생성한다.

stages 배열을 오름차순으로 정렬한다.

<스테이지, 실패율>을 키벨류 값으로 정해 해쉬맵을 만들어준다.

 

배열에 특정 정수가 몇개가 포함되어 있는지 구하는 메소드

private int countOfNum(int key, int[] arr, int startIndex){
        int count = 0;
        for(int i=startIndex; i<arr.length; i++){
            if(arr[i] == key) count++;
            else return count;
        }
        return count;
    }

이미 정렬되어 있는 배열의 특성을 이용하여 시작인덱스를 지정해 시간복잡도를 줄인다.

 

각 스테이지별 실패율을 해쉬맵에 입력

	...
        int loser = 0;
        for(int i=1; i<=N; i++){
            int count = countOfNum(i, stages, loser);
            int challenger = people - loser;
            if(challenger == 0){
                challenger=1;
            }
            hashMap.put(i, (double)count/(double)challenger);
            loser += count;
        }
        ...

1~N사이의 각 스테이지를 키값으로 하여 스테이지별 실패값을 입력한다.

count에 현재 스테이지(i)에 도전하는(실패한) 유저의 머릿수를 입력한다.

stages배열을 오름차 순으로 정렬한 이유는 예를들어 [1, 2, 3, 4]이 주어진다면 3스테이지 일때(index=2) 총 유저수에서 - loser

즉 도전 해보지도 못한 유저의 수를 빼주어 도전자의 수를 구해준다.

※아무도 최고 스테이지까지 도달하지 못하는 경우에 도전자가 0이게 되면 0으로 나누는 상황이 발생함으로 예외처리를 해준다.

 

자바의 컬랙션을 활용해 정답 구하기

	...
        List<Integer> keySetList = new ArrayList<>(hashMap.keySet());

        Collections.sort(keySetList, (o1, o2) -> (hashMap.get(o2).compareTo(hashMap.get(o1))));


        int[] answer = new int[N];
        int index = 0;
        for(Integer key : keySetList) {
            answer[index] = key;
            index++;
        }

        return answer;
    }

해쉬맵의 value값(실패율)으로 내림차순으로 정렬하여야 한다.

먼저 리스트에 해쉬맵의 키값들을 입력한다.

그 후 의 리스트의 정렬 조건을 해쉬맵의 value의 내림차순으로 해준다.

정렬된 리스트를 answer배열에 입력해 문제를 해결한다.

 

문제 설명

대구 달성공원에 놀러 온 지수는 최근에 새로 만든 타일 장식물을 보게 되었다. 타일 장식물은 정사각형 타일을 붙여 만든 형태였는데, 한 변이 1인 정사각형 타일부터 시작하여 마치 앵무조개의 나선 모양처럼 점점 큰 타일을 붙인 형태였다. 타일 장식물의 일부를 그리면 다음과 같다.

그림에서 타일에 적힌 수는 각 타일의 한 변의 길이를 나타낸다. 타일 장식물을 구성하는 정사각형 타일 한 변의 길이를 안쪽 타일부터 시작하여 차례로 적으면 다음과 같다.
[1, 1, 2, 3, 5, 8, .]
지수는 문득 이러한 타일들로 구성되는 큰 직사각형의 둘레가 궁금해졌다. 예를 들어, 처음 다섯 개의 타일이 구성하는 직사각형(위에서 빨간색으로 표시한 직사각형)의 둘레는 26이다.

타일의 개수 N이 주어질 때, N개의 타일로 구성된 직사각형의 둘레를 return 하도록 solution 함수를 작성하시오.

제한 사항

  • N은 1 이상 80 이하인 자연수이다.

 

해결

전체 코드부터 보자

class Solution {
    public long solution(int N) {
        long answer = 0;
        long n1 = 1;
        long n2 = 1;
        for(long i=0;i<N;i++){
            answer = n1 + n2;
            n1 = n2;
            n2 = answer;
        }
        return answer * 2;
    }
}

다음 직사각형의 한변의 길이는 현재와 이전의 타일 길이의 합이다. 이는 피보나치 수열과 흡사하다.

문제설명의 규칙을 보자 원하는 N개의 타일로 이루어진 직사각형의 짧은 변은 n1 긴 변은 n2의 피보나치 수로 이루어 져 있다.

N이 3인경우 n1은 2, n2는 3이다 즉 가로, 세로의 길이가 2, 3임으로 가로 세로의 합 * 2를 하면 둘레를 알 수 있다.

 

'알고리즘문제 > Java' 카테고리의 다른 글

2019 카카오 개발자 겨울 인턴십 - 튜플  (0) 2020.08.12
2019 카카오블라인드 : 실패율  (0) 2020.08.07
2018카카오블라인드 : 방금그곡  (0) 2019.12.05
2 * n 타일링  (0) 2019.12.05
구명보트  (0) 2019.12.05

문제 출처 : https://programmers.co.kr/learn/courses/30/lessons/17683

 

코딩테스트 연습 - [3차] 방금그곡 | 프로그래머스

방금그곡 라디오를 자주 듣는 네오는 라디오에서 방금 나왔던 음악이 무슨 음악인지 궁금해질 때가 많다. 그럴 때 네오는 다음 포털의 '방금그곡' 서비스를 이용하곤 한다. 방금그곡에서는 TV, 라디오 등에서 나온 음악에 관해 제목 등의 정보를 제공하는 서비스이다. 네오는 자신이 기억한 멜로디를 가지고 방금그곡을 이용해 음악을 찾는다. 그런데 라디오 방송에서는 한 음악을 반복해서 재생할 때도 있어서 네오가 기억하고 있는 멜로디는 음악 끝부분과 처음 부분이 이어

programmers.co.kr

문제 설명

방금그곡

라디오를 자주 듣는 네오는 라디오에서 방금 나왔던 음악이 무슨 음악인지 궁금해질 때가 많다. 그럴 때 네오는 다음 포털의 '방금그곡' 서비스를 이용하곤 한다. 방금그곡에서는 TV, 라디오 등에서 나온 음악에 관해 제목 등의 정보를 제공하는 서비스이다.

네오는 자신이 기억한 멜로디를 가지고 방금그곡을 이용해 음악을 찾는다. 그런데 라디오 방송에서는 한 음악을 반복해서 재생할 때도 있어서 네오가 기억하고 있는 멜로디는 음악 끝부분과 처음 부분이 이어서 재생된 멜로디일 수도 있다. 반대로, 한 음악을 중간에 끊을 경우 원본 음악에는 네오가 기억한 멜로디가 들어있다 해도 그 곡이 네오가 들은 곡이 아닐 수도 있다. 그렇기 때문에 네오는 기억한 멜로디를 재생 시간과 제공된 악보를 직접 보면서 비교하려고 한다. 다음과 같은 가정을 할 때 네오가 찾으려는 음악의 제목을 구하여라.

  • 방금그곡 서비스에서는 음악 제목, 재생이 시작되고 끝난 시각, 악보를 제공한다.

  • 네오가 기억한 멜로디와 악보에 사용되는 음은 C, C#, D, D#, E, F, F#, G, G#, A, A#, B 12개이다.

  • 각 음은 1분에 1개씩 재생된다. 음악은 반드시 처음부터 재생되며 음악 길이보다 재생된 시간이 길 때는 음악이 끊김 없이 처음부터 반복해서 재생된다. 음악 길이보다 재생된 시간이 짧을 때는 처음부터 재생 시간만큼만 재생된다.

  • 음악이 00:00를 넘겨서까지 재생되는 일은 없다.

  • 조건이 일치하는 음악이 여러 개일 때에는 라디오에서 재생된 시간이 제일 긴 음악 제목을 반환한다. 재생된 시간도 같을 경우 먼저 입력된 음악 제목을 반환한다.

  • 조건이 일치하는 음악이 없을 때에는 `(None)`을 반환한다.

입력 형식

입력으로 네오가 기억한 멜로디를 담은 문자열 m과 방송된 곡의 정보를 담고 있는 배열 musicinfos가 주어진다.

  • m은 음 1개 이상 1439개 이하로 구성되어 있다.

  • musicinfos는 100개 이하의 곡 정보를 담고 있는 배열로, 각각의 곡 정보는 음악이 시작한 시각, 끝난 시각, 음악 제목, 악보 정보가 ','로 구분된 문자열이다.

    • 음악의 시작 시각과 끝난 시각은 24시간 HH:MM 형식이다.

    • 음악 제목은 ',' 이외의 출력 가능한 문자로 표현된 길이 1 이상 64 이하의 문자열이다.

    • 악보 정보는 음 1개 이상 1439개 이하로 구성되어 있다.

출력 형식

조건과 일치하는 음악 제목을 출력한다.

 

 

해결

1. 문제 해석

시작시간, 종료시간, 제목, 멜로디 4가지 정보를 가지고 있는 문자열로 이루어진 배열과

사용자가 기억하고있는 멜로디를 가지고 사용자가 기억하는 노래의 제목을 찾는 문제이다.

 

2. 파싱

먼저 정보를 가지고있는 문자열을 파싱하여준다.

public String solution(String m, String[] musicinfos) {
    String answer = "";
    
    for (String s : musicinfos) { 
        String[] sArr = s.split(",");
        String[] sttArr = sArr[0].split(":");
        String[] edtArr = sArr[1].split(":");

        int playHour = Integer.parseInt(edtArr[0]) - Integer.parseInt(sttArr[0]);
        int playMin = Integer.parseInt(edtArr[1]) - Integer.parseInt(sttArr[1]) + (playHour * 60);
    }
    return answer;
}

문자열은 시작시간, 종료시간, 제목, 멜로디로 ","를 이용해 구분되어있다.

split(); 메서드를 이용해 소분 해준 뒤 시작시간, 종료시간을 구해준다.

시간 정보들은 시:분으로 구성되어있다. 이또한 split(); 를 이용해준다.

단 노래가 재생된 시간만큼 멜로디를 연산해야 하기때문에 parseInt를 이용해 Integer형으로 바꿔준다.

시간에 60을 곱해 분으로 바꿔준뒤 총 재생시간(분)을 구한다.

 

3. 재생된 멜로디구간 만들기

앞서 구한 총 재생시간으로 노래가 재생된 구간을 만들어 주는 매서드를 생성한다.

private String makeMusic(String s, int t) {
    StringBuilder sb= new StringBuilder();
    for (int i = 0; i < t; i++) {
        sb.append(s.charAt(i % s.length()));
    }
    return sb.toString();
}

노래의 멜로디와 재생시간을 파라미터로 받아 시간만큼 재생된 멜로디를 문자열로 반환한다.

String music = makeMusic(sArr[3], playMin);

solution 메서드에 추가해준다.

 

 

4. 멜로디 검사

이제 사용자가 기억하는 멜로디 m과 만들어진 music을 비교해 찾는 노래가 맞는지 확인해야 한다.

if (music.contains(m)) {
    if (maxPT < playMin) {
    maxPT = playMin;
    answer = sArr[2];
    }
}

노래안에 해당 멜로디가 있으면 노래를 찾은것이다. 단 멜로디가 여러 노래에 포함 될 경우 가장 플레이타임이 긴 노래를 반환 해준다.

 

5. 까다로운 조건

노래의 음표에 반음이 포함되어있다. 지금껏 작성한 코드는 멜로디 및 노래에 "#"이 포함되지 않는다면 잘 작동한다.

하지만 "#"이 있을경우는 조금 문제가 생긴다.

예를들어 사용자가 기억한 멜로디가 "ABC" 노래가 "ABC#"이면 위 코드에서 contain 메소드가 true값을 반환 하지만

사실은 C와 C#은 완전히 다르다.

replaceAll메서드를 이용해 다른 문자로 변환하여 처리하자.

private String killSharp(String s) {
    s = s.replaceAll("A#", "a");
    s = s.replaceAll("C#", "c");
    s = s.replaceAll("D#", "d");
    s = s.replaceAll("F#", "f");
    s = s.replaceAll("G#", "g");
    return s;
}

"#"이 달린 음은 소문자로 처리를 해 준다.

노래와 멜로디 둘다 이러한 작업을 해 주면 정상 작동한다.

 

전체소스

class Solution {
    public String solution(String m, String[] musicinfos) {
        String answer = "'(None)'";
        int maxPT = 0;
        m = killSharp(m);
        for (String s : musicinfos) {
            s = killSharp(m);

            String[] sArr = s.split(",");
            String[] sttArr = sArr[0].split(":");
            String[] edtArr = sArr[1].split(":");

            int playHour = Integer.parseInt(edtArr[0]) - Integer.parseInt(sttArr[0]);
            int playMin = Integer.parseInt(edtArr[1]) - Integer.parseInt(sttArr[1]) + (playHour * 60);

            String music = makeMusic(sArr[3], playMin);

            if (music.contains(m)) {
                if (maxPT < playMin) {
                    maxPT = playMin;
                    answer = sArr[2];
                }
            }
        }
        return answer;
    }

    private String makeMusic(String s, int t) {
        StringBuilder sb= new StringBuilder();
        for (int i = 0; i < t; i++) {
            sb.append(s.charAt(i % s.length()));
        }
        return sb.toString();
    }

    private String killSharp(String s) {
        s = s.replaceAll("A#", "a");
        s = s.replaceAll("C#", "c");
        s = s.replaceAll("D#", "d");
        s = s.replaceAll("F#", "f");
        s = s.replaceAll("G#", "g");
        return s;
    }
}

 

 

'알고리즘문제 > Java' 카테고리의 다른 글

2019 카카오블라인드 : 실패율  (0) 2020.08.07
타일 장식물  (0) 2019.12.06
2 * n 타일링  (0) 2019.12.05
구명보트  (0) 2019.12.05
2019카카오블라인드 : 오픈채팅방  (0) 2019.12.04

문제출처 : https://programmers.co.kr/learn/courses/30/lessons/12900

 

코딩테스트 연습 - 2 x n 타일링 | 프로그래머스

가로 길이가 2이고 세로의 길이가 1인 직사각형모양의 타일이 있습니다. 이 직사각형 타일을 이용하여 세로의 길이가 2이고 가로의 길이가 n인 바닥을 가득 채우려고 합니다. 타일을 채울 때는 다음과 같이 2가지 방법이 있습니다. 타일을 가로로 배치 하는 경우 타일을 세로로 배치 하는 경우 예를들어서 n이 7인 직사각형은 다음과 같이 채울 수 있습니다. 직사각형의 가로의 길이 n이 매개변수로 주어질 때, 이 직사각형을 채우는 방법의 수를 return 하는 s

programmers.co.kr

문제설명

가로 길이가 2이고 세로의 길이가 1인 직사각형모양의 타일이 있습니다. 이 직사각형 타일을 이용하여 세로의 길이가 2이고 가로의 길이가 n인 바닥을 가득 채우려고 합니다. 타일을 채울 때는 다음과 같이 2가지 방법이 있습니다.

  • 타일을 가로로 배치 하는 경우

  • 타일을 세로로 배치 하는 경우

예를들어서 n이 7인 직사각형은 다음과 같이 채울 수 있습니다.

직사각형의 가로의 길이 n이 매개변수로 주어질 때, 이 직사각형을 채우는 방법의 수를 return 하는 solution 함수를 완성해주세요.

제한사항

  • 가로의 길이 n은 60,000이하의 자연수 입니다.

  • 경우의 수가 많아 질 수 있으므로, 경우의 수를 1,000,000,007으로 나눈 나머지를 return해주세요.

 

해결

1. 문제분석

해당 문제는 가로세로 1*2 크기의 타일을 2 * n 크기의 바닥에 채우는 경우의 수를 구하는 문제이다. 

앞에 놓을 타일이 세로로 있는 경우의 수와 가로로 2개 놓여있는 경우의 수를 더해가며 답을 찾아가면 된다.

(1) n이 1인 경우는 세로 1. = 1

(2) n이 2인 경우는 ((1) + (1)) +  (가로 2) = 2

(3) n이 3인 경우는 ((1) + ((1) + (1)) ||| (2))..... = 3

즉 과거에 놓은 경우의 수 + 현재 놓은 경우의 수를 알면 다음 경우의수를 구할 수 있다.

 

2. 설명

전체 소스를 먼저 보자

class Solution {
    public int solution(int n) {
        int answer = 0, n1 = 0, n2 = 1;
      
        for(int i=0;i<n;i++){
            answer = (n1 + n2) % 1000000007;
            n1 = n2;
            n2 = answer;
        }
        return answer;
    }
}

이전 경우의 수 (n1)과 현재 경우의 수 (n2)를 더하면 다음번에 오는 경우의 수를 알 수 있다.

그리고 다다음번의 수를 알기위한 연산을 위해 각각 n1 < n2 < answer 을 대입해 준다.

반복문으로 n번의 연산 후 answer에는 n-2, n-1의 경우의 수의 합이 대입된다.

마치 피보나치 수를 구하는 코드와 같다.

 

셀프 코드리뷰

해당 문제는 level 3치고는 쉬운 문제같다.

문제의 패턴과 해결방법을 간파하는 시간에 비해 코드 작성시간이 짧았다.

 

'알고리즘문제 > Java' 카테고리의 다른 글

타일 장식물  (0) 2019.12.06
2018카카오블라인드 : 방금그곡  (0) 2019.12.05
구명보트  (0) 2019.12.05
2019카카오블라인드 : 오픈채팅방  (0) 2019.12.04
2020카카오공채 : 자물쇠와 열쇠  (0) 2019.12.04

문제 출처 : https://programmers.co.kr/learn/courses/30/lessons/42885

 

코딩테스트 연습 - 구명보트 | 프로그래머스

무인도에 갇힌 사람들을 구명보트를 이용하여 구출하려고 합니다. 구명보트는 작아서 한 번에 최대 2명씩 밖에 탈 수 없고, 무게 제한도 있습니다. 예를 들어, 사람들의 몸무게가 [70kg, 50kg, 80kg, 50kg]이고 구명보트의 무게 제한이 100kg이라면 2번째 사람과 4번째 사람은 같이 탈 수 있지만 1번째 사람과 3번째 사람의 무게의 합은 150kg이므로 구명보트의 무게 제한을 초과하여 같이 탈 수 없습니다. 구명보트를 최대한 적게 사용하여 모

programmers.co.kr

문제 설명

무인도에 갇힌 사람들을 구명보트를 이용하여 구출하려고 합니다. 구명보트는 작아서 한 번에 최대 2명씩밖에 탈 수 없고, 무게 제한도 있습니다.

예를 들어, 사람들의 몸무게가 [70kg, 50kg, 80kg, 50kg]이고 구명보트의 무게 제한이 100kg이라면 2번째 사람과 4번째 사람은 같이 탈 수 있지만 1번째 사람과 3번째 사람의 무게의 합은 150kg이므로 구명보트의 무게 제한을 초과하여 같이 탈 수 없습니다.

구명보트를 최대한 적게 사용하여 모든 사람을 구출하려고 합니다.

사람들의 몸무게를 담은 배열 people과 구명보트의 무게 제한 limit가 매개변수로 주어질 때, 모든 사람을 구출하기 위해 필요한 구명보트 개수의 최솟값을 return 하도록 solution 함수를 작성해주세요.

 

제한사항

  • 무인도에 갇힌 사람은 1명 이상 50,000명 이하입니다.

  • 각 사람의 몸무게는 40kg 이상 240kg 이하입니다.

  • 구명보트의 무게 제한은 40kg 이상 240kg 이하입니다.

  • 구명보트의 무게 제한은 항상 사람들의 몸무게 중 최댓값보다 크게 주어지므로 사람들을 구출할 수 없는 경우는 없습니다.

 

해결

1. 문제분석

해당 문제는 최적의 해를 구하는 탐욕(greedy) 알고리즘 문제이다.

구명보트에 태울 수 있는 한 사람을 꽉꽉 채워 최소의 구명보트 개수를 구하는 문제이다.

 

2. 정렬

사람의 몸무게를 일일이 비교해가며 보트에 태워야 한다.

시간 복잡도를 줄이고 코드를 보기 편하게 짜기 위해 정렬을 해준다.

import java.util.Arrays;
class Solution {
    public int solution(int[] people, int limit) {
        int answer = 0;
        
        Arrays.sort(people);

        return answer;
    }
}

자바 컬랙션인 "Arrays"의 정렬 메서드를 이용해 배열을 오름차순 정렬해준다.

 

3. 알고리즘

전체 소스 먼저 보자

import java.util.Arrays;
class Solution {
    public int solution(int[] people, int limit) {
        int answer = people.length;

        Arrays.sort(people);

        int l = 0;
        int r = people.length-1;

        while(l<r) {
            if (people[l] + people[r] <= limit) l++;
            r --;
        }
        
        return answer - l;
    }
}

가장 무거운 사람부터 먼저 태운다고 생각하겠다.

가장 무거운 사람을 태운 뒤 빈자리를 가벼운 사람들로 채운다면 최소한의 보트 수로 무인도를 탈출할 수 있다. - (1)

배열의 시작과 끝 값을 각각 l, r로 본다.

반복문이 끝나는 시점은!(l <r) 즉 모든 사람이 보트에 타 무인도를 탈출하는 시점이다.

if (people[l] + people[r] <= limit) l++;
r--

가장 가벼운 사람과 가장 무거운 사람의 무게 합이 최대 수용 무게보다 낮으면 태운다.

그 뒤에 무거운 사람을 태운다. (1)의 말과는 이야기 흐름상 다르지만 비교 연산을 위한 것이다.

예를 들어 모든 사람 30명의 무게가 100kg이고 보트의 최대 수용 무게가 100kg인 경우 보트는 30대가 필요하다.

 

즉 보트에 사람을 1명 더 태울수록 필요한 보트의 개수가 1 줄어든다(l++).

줄어든 만큼 최악의 경우(answer = people.length)에서 추가로 태운 횟수(l)를 빼 준 뒤 값을 반환한다.

 

셀프 코드 리뷰

Level 2의 쉬운 난이도의 문제였다.

굳이 answer변수를 선언해주지 않아도 됐을 것 같다.

'알고리즘문제 > Java' 카테고리의 다른 글

2018카카오블라인드 : 방금그곡  (0) 2019.12.05
2 * n 타일링  (0) 2019.12.05
2019카카오블라인드 : 오픈채팅방  (0) 2019.12.04
2020카카오공채 : 자물쇠와 열쇠  (0) 2019.12.04
한수  (0) 2019.12.03

문제출처 : https://programmers.co.kr/learn/courses/30/lessons/42888

 

코딩테스트 연습 - 오픈채팅방 | 프로그래머스

오픈채팅방 카카오톡 오픈채팅방에서는 친구가 아닌 사람들과 대화를 할 수 있는데, 본래 닉네임이 아닌 가상의 닉네임을 사용하여 채팅방에 들어갈 수 있다. 신입사원인 김크루는 카카오톡 오픈 채팅방을 개설한 사람을 위해, 다양한 사람들이 들어오고, 나가는 것을 지켜볼 수 있는 관리자창을 만들기로 했다. 채팅방에 누군가 들어오면 다음 메시지가 출력된다. [닉네임]님이 들어왔습니다. 채팅방에서 누군가 나가면 다음 메시지가 출력된다. [닉네임]님이 나갔습니다. 채팅

programmers.co.kr

 

문제 설명

카카오톡 오픈채팅방에서는 친구가 아닌 사람들과 대화를 할 수 있는데, 본래 닉네임이 아닌 가상의 닉네임을 사용하여 채팅방에 들어갈 수 있다.

신입사원인 김크루는 카카오톡 오픈 채팅방을 개설한 사람을 위해, 다양한 사람들이 들어오고, 나가는 것을 지켜볼 수 있는 관리자창을 만들기로 했다. 채팅방에 누군가 들어오면 다음 메시지가 출력된다.

[닉네임]님이 들어왔습니다.

채팅방에서 누군가 나가면 다음 메시지가 출력된다.

[닉네임]님이 나갔습니다.

채팅방에서 닉네임을 변경하는 방법은 다음과 같이 두 가지이다.

  • 채팅방을 나간 후, 새로운 닉네임으로 다시 들어간다.

  • 채팅방에서 닉네임을 변경한다.

닉네임을 변경할 때는 기존에 채팅방에 출력되어 있던 메시지의 닉네임도 전부 변경된다.

예를 들어, 채팅방에 Muzi와 Prodo라는 닉네임을 사용하는 사람이 순서대로 들어오면 채팅방에는 다음과 같이 메시지가 출력된다.

Muzi님이 들어왔습니다.
Prodo님이 들어왔습니다.

채팅방에 있던 사람이 나가면 채팅방에는 다음과 같이 메시지가 남는다.

Muzi님이 들어왔습니다.
Prodo님이 들어왔습니다.
Muzi님이 나갔습니다.

Muzi가 나간후 다시 들어올 때, Prodo 라는 닉네임으로 들어올 경우 기존에 채팅방에 남아있던 Muzi도 Prodo로 다음과 같이 변경된다.

Prodo님이 들어왔습니다.
Prodo님이 들어왔습니다.
Prodo님이 나갔습니다.
Prodo님이 들어왔습니다.

채팅방은 중복 닉네임을 허용하기 때문에, 현재 채팅방에는 Prodo라는 닉네임을 사용하는 사람이 두 명이 있다. 이제, 채팅방에 두 번째로 들어왔던 Prodo가 Ryan으로 닉네임을 변경하면 채팅방 메시지는 다음과 같이 변경된다.

Prodo님이 들어왔습니다.
Ryan님이 들어왔습니다.
Prodo님이 나갔습니다.
Prodo님이 들어왔습니다.

채팅방에 들어오고 나가거나, 닉네임을 변경한 기록이 담긴 문자열 배열 record가 매개변수로 주어질 때, 모든 기록이 처리된 후, 최종적으로 방을 개설한 사람이 보게 되는 메시지를 문자열 배열 형태로 return 하도록 solution 함수를 완성하라.

 

제한사항

  • record는 다음과 같은 문자열이 담긴 배열이며, 길이는 1 이상 100,000 이하이다.

  • 다음은 record에 담긴 문자열에 대한 설명이다.

    • 모든 유저는 [유저 아이디]로 구분한다.

    • [유저 아이디] 사용자가 [닉네임]으로 채팅방에 입장 - Enter [유저 아이디] [닉네임] (ex. Enter uid1234 Muzi)

    • [유저 아이디] 사용자가 채팅방에서 퇴장 - Leave [유저 아이디] (ex. Leave uid1234)

    • [유저 아이디] 사용자가 닉네임을 [닉네임]으로 변경 - Change [유저 아이디] [닉네임] (ex. Change uid1234 Muzi)

    • 첫 단어는 Enter, Leave, Change 중 하나이다.

    • 각 단어는 공백으로 구분되어 있으며, 알파벳 대문자, 소문자, 숫자로만 이루어져있다.

    • 유저 아이디와 닉네임은 알파벳 대문자, 소문자를 구별한다.

    • 유저 아이디와 닉네임의 길이는 1 이상 10 이하이다.

    • 채팅방에서 나간 유저가 닉네임을 변경하는 등 잘못 된 입력은 주어지지 않는다.

 

해결

1. 문제 분석

 

1) 해당 문제는 (상태 고유아이디 닉네임)으로 이루어진 문자열의 배열이 입력된다.

2) 또한 3가지 입력 중 채팅방에 들어오고 나가는 2가지 입력과 닉네임만 출력해야 한다.

3) Leave는 닉네임값이 들어있지 않고, Change는 출력되지 않는다.

 

 

2. 닉네임은 변경되어도 고유 아이디는 변하지 않는다.

 

이를 (아이디 - 닉네임)을 (키 - 벨류)라 생각하고 수월하게 처리하기위해 해시맵과 리스트를 사용한다.

public String[] solution(String[] record) {
    HashMap<String,String> hm = new HashMap<String,String>();
    ArrayList<String> sList = new ArrayList<String>();
}

 

3. 문자열 파싱

 

record라는 문자열 배열이 파라미터로 주어졌고 한 줄에 들어있는 정보를 확인해야 한다.

이를 나누어 어떠한 사용자가 입퇴장 했는지, 닉네임을 변경했는지 알아야 한다.

String.split();을 사용한다.

for(String s : record){
    String[] tempSArr = s.split(" ");
    if(tempSArr[0].equals("Enter") || tempSArr[0].equals("Change")) hm.put(tempSArr[1], tempSArr[2]);
}

record에 들어있는 하나의 값을 tempSArr에 " "로 구분하여 저장한다.

이렇게 되면 tempSArr = {상태, 고유아이디, 닉네임}으로 구분할 수 있다.(상태가 퇴장하는 leave인 경우 닉네임이 들어갈 [2]번 인덱스가 존재하지 않음.)

"Leave" 상태는 출력시에만 사용함으로 "Enter", "Change" 의 경우에 해시맵에 put 해준다. 이러면 나중에 넣는값 즉

입장 후 닉네임을 변경한 값이 저장된다.

 

 

4. 단어생성 메서드 "makeWord"

 

출력 문자열은 "<닉네임>님이 <입,퇴장>하였습니다." 로 이루어져야 한다.

상태정보를 가지고 있는 문자열 배열과 닉네임 정보를 가지고있는 해시맵을 파라미터로 받는다. 

private String makeWord(String[] sArr, HashMap hm){
    String tempS = hm.get(sArr[1]) + "님이 ";

    if (sArr[0].equals("Enter")) tempS += "들어왔습니다.";
    else if (sArr[0].equals("Leave")) tempS += "나갔습니다.";
    
    return tempS;
}

[1]에 저장된 닉네임을 [0]의 값을 이용해 입, 퇴장 문자를 입력해준다.

 

 

5. 리스트 변환

 

for(String s : record){
    String[] tempSArr = s.split(" ");
    if(!tempSArr[0].equals("Change")) {
        String tempS = makeWord(tempSArr, hm);
        sList.add(tempS);
    }
}

String[] answer = sList.toArray(new String[sList.size()]);
return answer;

Change 상태의 경우 출력에 포함되지 않음으로 제외 시키고 makeWord 메서드에 보내준다.

makeWord 메서드에서 반환된 문자열을 tempS에 넣어준 후 리스트에 add 해준다.

 

리스트를 answer 배열로 변환 후 리턴한다.

 

 

전체 코드

import java.util.ArrayList;
import java.util.HashMap;

class Solution {
    public String[] solution(String[] record) {
        HashMap<String,String> hm = new HashMap<String,String>();
        ArrayList<String> sList = new ArrayList<String>();

        for(String s : record){
            String[] tempSArr = s.split(" ");
            if(tempSArr[0].equals("Enter") || tempSArr[0].equals("Change")) hm.put(tempSArr[1], tempSArr[2]);
        }

        for(String s : record){
            String[] tempSArr = s.split(" ");
            if(!tempSArr[0].equals("Change")) {
                String tempS = makeWord(tempSArr, hm);
                sList.add(tempS);
            }
        }

        String[] answer = sList.toArray(new String[sList.size()]);
        return answer;
    }
    
    private String makeWord(String[] sArr, HashMap hm){
        String tempS = hm.get(sArr[1]) + "님이 ";

        if (sArr[0].equals("Enter")) tempS += "들어왔습니다.";
        else if (sArr[0].equals("Leave")) tempS += "나갔습니다.";

        return tempS;
    }
}

 

 

셀프 코드리뷰

level 2의 쉬운 난이도의 문제 치고 코드가 깔끔하지 못하다.

같은 코드가 반복되는 구간이 있고 조건문도 더 간략하고 빠르게 작성하는 법도 있을것이다.

'알고리즘문제 > Java' 카테고리의 다른 글

2018카카오블라인드 : 방금그곡  (0) 2019.12.05
2 * n 타일링  (0) 2019.12.05
구명보트  (0) 2019.12.05
2020카카오공채 : 자물쇠와 열쇠  (0) 2019.12.04
한수  (0) 2019.12.03

+ Recent posts