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

 

코딩테스트 연습 - 단어 변환

두 개의 단어 begin, target과 단어의 집합 words가 있습니다. 아래와 같은 규칙을 이용하여 begin에서 target으로 변환하는 가장 짧은 변환 과정을 찾으려고 합니다. 1. 한 번에 한 개의 알파벳만 바꿀 수

programmers.co.kr

시작문자와 목표문자 String begin, target 과 사용할 수 있는 단어들의 배열 String[] words 가 주어진다.

 

begin을 시작으로 하여 words의 원소들로 적절히 단어를 바꿔가며 target에 도달할 수 있는 최소 변경 수를 반환해야한다.

 

단, 단어 변경은 알파벳이 하나 차이나는 단어로만 변경이 가능하며, target에 도달이 불가한 경우는 0을 반환한다.

 

 

 

풀이 .

import java.util.*;

class Solution {
    int answer = 51;
    boolean[] check = null;

    public boolean isPossible(String from, String to) {
        int cnt = 0, len = from.length();
        for(int i = 0; i < len; i++) {
            if(from.charAt(i) == to.charAt(i)) {
                cnt++;
            }
        }
        return cnt == len - 1 ? true : false;
    }


    public void dfs(int dept, String now, String target, String[] words) {
        if(now.equals(target)) {
            answer = Math.min(answer, dept);
            return;
        }

        for(int i = 0; i < words.length; i++) {
            String next = words[i];
            if(check[i] == false && isPossible(now, next)) {
                check[i] = true;
                dfs(dept + 1, next, target, words);
                check[i] = false;
            }
        }
    }

    public int solution(String begin, String target, String[] words) {
        check = new boolean[words.length];

        for(int i = 0; i < words.length; i++) {
            String next = words[i];
            if(check[i] == false && isPossible(begin, words[i])) {
                check[i] = true;
                dfs(1, next, target, words);
                check[i] = false;
            }
        }

        return answer == 51 ? 0 : answer;
    }
}

DFS로 풀었다.

words.length의 최대값이 50이므로 answer의 초기값을 51로 잡았다.

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

[PG] 입국심사 JAVA  (0) 2021.01.04
[PG] 여행경로 JAVA  (0) 2021.01.04
[PG] 네트워크 JAVA  (0) 2021.01.04
[PG] 타겟 넘버 JAVA  (0) 2021.01.03
[PG] 정수 삼각형 JAVA  (0) 2021.01.03

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

 

코딩테스트 연습 - 네트워크

네트워크란 컴퓨터 상호 간에 정보를 교환할 수 있도록 연결된 형태를 의미합니다. 예를 들어, 컴퓨터 A와 컴퓨터 B가 직접적으로 연결되어있고, 컴퓨터 B와 컴퓨터 C가 직접적으로 연결되어 있

programmers.co.kr

노드의 개수 int n, 노드 간 연결상태를 인접행렬로 나타낸 int[][] computers 가 주어진다.

 

component의 개수를 반환해야한다.

 

 

 

풀이 1.

import java.util.*;

class Solution {
    boolean[] check = null;

    public void dfs(int node, int[][] computers) {
        check[node] = true;

        for(int i = 0; i < computers[node].length; i++) {
            if(computers[node][i] == 1 && check[i] == false) {
                dfs(i, computers);
            }
        }
    }

    public void bfs(int start, int[][] computers) {
        Queue<Integer> que = new LinkedList<>();
        que.add(start);

        while(!que.isEmpty()) {
            int node = que.poll();
            for(int i = 0; i < computers[node].length; i++) {
                if(computers[node][i] == 1 && check[i] == false) {
                    que.add(i);
                    check[i] = true;
                }
            }
        }
    }


    public int solution(int n, int[][] computers) {
        int answer = 0;
        check = new boolean[computers.length];

        for(int i = 0; i < computers.length; i++) {
            if(check[i] == false) {
                dfs(i, computers);
//                bfs(i, computers);
                answer++;
            }
        }

        return answer;
    }
}

주어진 인접행렬을 그대로 사용했다.

 

 

 

풀이 2.

import java.util.*;

class Solution {
    ArrayList<Integer>[] list = null;
    boolean[] check = null;

    public void dfs(int node) {
        check[node] = true;

        for (int i = 0; i < list[node].size(); i++) {
            int next = list[node].get(i);
            if (check[next] == false) {
                dfs(next);
            }
        }
    }

    public void bfs(int start) {
        Queue<Integer> que = new LinkedList<Integer>();
        que.add(start);
        check[start] = true;

        while (!que.isEmpty()) {
            int node = que.poll();
            for (int i = 0; i < list[node].size(); i++) {
                int next = list[node].get(i);
                if (check[next] == false) {
                    que.add(next);
                    check[next] = true;
                }
            }
        }
    }

    public int solution(int n, int[][] computers) {
        int answer = 0;
        list = new ArrayList[n + 1];
        check = new boolean[n + 1];

        for (int i = 1; i <= n; i++) {
            list[i] = new ArrayList<Integer>();
        }

        for (int i = 0; i < computers.length; i++) {
            for (int j = 0; j < computers[i].length; j++) {
                if (computers[i][j] == 1) {
                    list[i + 1].add(j + 1);
                }
            }
        }

        for (int i = 1; i <= n; i++) {
            if (check[i] == false) {
                dfs(i);
// 				bfs(i);
                answer++;
            }
        }

        return answer;
    }
}

인접리스트로 고쳐서 풀 수도 있다.

 

 

 

 

 

전형적인 DFS, BFS 문제라 딱히 쓸 말이 없다.

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

[PG] 여행경로 JAVA  (0) 2021.01.04
[PG] 단어 변환 JAVA  (0) 2021.01.04
[PG] 타겟 넘버 JAVA  (0) 2021.01.03
[PG] 정수 삼각형 JAVA  (0) 2021.01.03
[PG] N으로 표현 JAVA  (0) 2021.01.03

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

 

코딩테스트 연습 - 타겟 넘버

n개의 음이 아닌 정수가 있습니다. 이 수를 적절히 더하거나 빼서 타겟 넘버를 만들려고 합니다. 예를 들어 [1, 1, 1, 1, 1]로 숫자 3을 만들려면 다음 다섯 방법을 쓸 수 있습니다. -1+1+1+1+1 = 3 +1-1+1+1+

programmers.co.kr

사용할 수 있는 숫자들의 배열 int[] numbers, int target 가 주어진다.

numbers 의 원소들을 사용해 덧셈, 뺄셈 연산을 적절히 수행했을 때 target을 만들어낼 수 있는 모든 경우의 수의 개수를 반환해야한다.

numbers의 원소는 모두 음이 아닌 정수이다.

 

 

 

풀이 1. (틀린 코드)

class Solution {
    // numbers의 원소들의 순서를 유지하지 않는 경우(모든 순열 구하는 방식)
    // 이 문제에선 틀린 코드임 (시간초과)
    
    int answer = 0;
    boolean[] check = null;
    
    public void DFS(int dept, int[] numbers, int target, int calc) {
        if(dept == numbers.length) {
            if(calc == target) {
                answer++;
            }
            return;
        }
        
        // 모든 순열을 구하기 위해 백트래킹 사용
        for(int i = 0; i < numbers.length; i++) {
            if(check[i] == false) {
                check[i] = true;
                DFS(dept + 1, numbers, target, calc + numbers[i]);
                DFS(dept + 1, numbers, target, calc - numbers[i]);
                check[i] = false;
            }
        }
    }
    
    public int solution(int[] numbers, int target) {
        check = new boolean[numbers.length];
        DFS(0, numbers, target, 0);
        return answer;
    }
}

 

문제의 조건이 제대로 명시되지 않아 틀린 코드를 작성했다.

 

numbers의 원소들이 사용되는 순서에 대한 아무런 언급이 없었기에 당연하게 원소들의 순서가 유지되지 않는, 즉, 모든 순열을 고려하는 방식으로 코드를 작성했다. 

 

그리고 시간초과를 맞이했다. numbers의 최대 길이는 20이다. + 연산만 하는 경우라고 해도 최대 20! 개의 식을 구하게 된다. 당연히 시간초과...

(물론 + 연산만 하면 모든 식의 결과가 같을 테니 무의미하긴 하지만 식의 개수에 대해 얘기하는 것이니..)

 

처음부터 이 계산을 해보고 뭔가 이상하단 걸 눈치 챘어야 했는데 무식하게 그냥 무작정 코딩했다. 

 

 

 

풀이 2. (정답 코드)

class Solution {
    int answer = 0;

    public void dfs(int dept, int calc, int[] numbers, int target) {
        if (dept == numbers.length) {  // 모든 원소를 반드시 전부 사용해야한다
            if (calc == target) {
                this.answer++;
            }
            return;
        }

        // 배열 내의 순서를 그대로 유지하며 +, - 연산을 이어나간다
        dfs(dept + 1, calc + numbers[dept], numbers, target);
        dfs(dept + 1, calc - numbers[dept], numbers, target);
    }

    public int solution(int[] numbers, int target) {
        dfs(0, 0, numbers, target);
        return this.answer;
    }
}

정답 코드이다.

 

아마 덧셈, 뺄셈은 항의 순서가 식의 결과에 영향을 미치지 않기 때문에 굳이 모든 순열을 구하지 않아도 되는 식으로 문제를 만든 것 같은데.. 그에 대한 설명이 너무 부실했다.

 

또, 배열의 모든 원소들을 반드시 다 사용하여야 한다는 규칙도 명시되지 않았다.

(이건 테스트 케이스에 간접적으로 드러나긴 했지만 그래도 따로 명시했어야 한다고 생각)

 

 

프로그래머스 문제는 이런 경우가 빈번히 발생하는 듯하다.

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

[PG] 단어 변환 JAVA  (0) 2021.01.04
[PG] 네트워크 JAVA  (0) 2021.01.04
[PG] 정수 삼각형 JAVA  (0) 2021.01.03
[PG] N으로 표현 JAVA  (0) 2021.01.03
[PG] 구명보트 JAVA  (0) 2021.01.03

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

 

코딩테스트 연습 - 정수 삼각형

[[7], [3, 8], [8, 1, 0], [2, 7, 4, 4], [4, 5, 2, 6, 5]] 30

programmers.co.kr

삼각형 모양으로 쌓여있는 수를 나타낸 이차원 배열 int[][] triangle이 주어진다.

맨 위 꼭지점을 시작 위치로 하여 삼각형 밑면에 도착할 수 있는 여러가지 경로가 존재한다.

각 경로별로 길을 지나며 마주치는 숫자들을 더해나갔을 때 만들 수 있는 가장 큰 수를 반환해야한다.

 

단, 현재 위치에서 왼쪽 아래, 오른쪽 아래 둘 중 하나의 길로만 내려갈 수 있다. 올라가거나 옆으로 가는 경로는 존재하지 않는다.

 

 

풀이 .

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

        // 초기화
        int[][] dp = new int[triangle.length][triangle.length];
        dp[0][0] = triangle[0][0];
        for (int i = 1; i < triangle.length; i++) {
            dp[i][0] = dp[i - 1][0] + triangle[i][0];  // 왼쪽 빗변 경로
            dp[i][i] = dp[i - 1][i - 1] + triangle[i][i];  // 오른쪽 빗변 경로
        }

        for(int i = 2; i < triangle.length; i++) {
            for(int j = 1; j < i ; j++) {
                dp[i][j] = Math.max(dp[i - 1][j - 1], dp[i - 1][j]) + triangle[i][j];
            }
        }

        // 마지막 행만 검사하면 된다
        for(int i = 0; i < triangle.length; i++) {
            if(answer < dp[triangle.length - 1][i]) {
                answer = dp[triangle.length - 1][i];
            }
        }
        return answer;
    }
}

전형적인 DP문제이다. 브루트 포스로 풀려면 n중 for문을 사용해야 하는데 n은 최대 500이다. 무조건 시간초과.

 

dp[i][j] 는 [i][j] 까지 도착할 수 있는 모든 경로들 중 최대값을 저장하도록 한다.

 

주어진 그림은 정삼각형이지만 2차원 배열의 행들을 그대로 나열해보면 직각삼각형이 된다.

이 형태에서 힌트를 얻어 초기화를 할 수 있다.

 

정삼각형이라고 봤을 때 양 옆 빗면을 그대로 따라가는 경로 2가지를 dp를 돌리기 전에 바로 구할 수 있게 된다.

그 후 2중 for문을 통해 dp를 돌렸다.

이때 for ( int j ) 의 범위를 ( j < triangle.length ) 로 잡아도 잘 돌아가긴 하지만 그래도 최대한 시간을 단축시켜보려고    ( j < i ) 로 잡았다.

 

이제 모든 경로의 도착지점인 마지막 행의 열들을 검사하여 최대값을 알아낼 수 있다.

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

[PG] 네트워크 JAVA  (0) 2021.01.04
[PG] 타겟 넘버 JAVA  (0) 2021.01.03
[PG] N으로 표현 JAVA  (0) 2021.01.03
[PG] 구명보트 JAVA  (0) 2021.01.03
[PG] 큰 수 만들기 JAVA  (0) 2021.01.03

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

 

코딩테스트 연습 - N으로 표현

 

programmers.co.kr

사용할 숫자 int N, 만들어야하는 숫자 int number 가 주어진다.

N을 사용해 만든 사칙연산 식의 결과가 number이 되도록 할 때 N의 최소 사용횟수를 반환해야한다.

 

1. NN의 형태는 N을 두 번 사용한 것이다. ex : 55 -> 5를 두 번 사용)

2. 최소값이 8보다 클 경우 -1을 반환한다.

 

 

풀이 .

class Solution {
    int answer = -1;

    public void dfs(int cnt, int calc, int N, int number) {
        if(calc == number) {
            if(answer == -1 || answer > cnt) {  // 정답이 한 번도 들어온 적이 없거나 최소값을 갱신한 경우
                answer = cnt;
            }
            return;
        }

        int temp = N;
        for(int i = 1; i <= 8 - cnt; i++) {  // N이 8개까지만 사용되도록 함
            dfs(cnt + i, calc + temp, N, number);
            dfs(cnt + i, calc - temp, N, number);
            dfs(cnt + i, calc * temp, N, number);
            dfs(cnt + i, calc / temp, N, number);

            temp = temp * 10 + N;  // N을 하나 더 붙임
        }
    }

    public int solution(int N, int number) {
        dfs(0, 0, N, number);
        return this.answer;
    }
}

분류는 DP이지만 DFS로 풀었다. 솔직히 어떻게 DP로 풀 수 있는지도 모르겠다.

 

int cnt를 사용해 반복문 내에서 사용할 수 있는 N의 개수를 제한했다.

 

정답 처리는 됐지만 사실 틀린 코드이다.

5*5+5/5 처럼 사칙연산의 규칙에 의해 연산자의 순서보다 *, / 연산자를 먼저 계산하는 경우는 고려할 수 없기 때문이다.

 

문제에 "연산자는 무조건 들어온 순서대로 계산된다." 라는 조건이 있어야만 온전히 정답일 수 있는 코드.

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

[PG] 타겟 넘버 JAVA  (0) 2021.01.03
[PG] 정수 삼각형 JAVA  (0) 2021.01.03
[PG] 구명보트 JAVA  (0) 2021.01.03
[PG] 큰 수 만들기 JAVA  (0) 2021.01.03
[PG] 조이스틱 JAVA  (0) 2021.01.02

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

 

코딩테스트 연습 - 구명보트

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

programmers.co.kr

사람들의 몸무게가 담긴 int[] people, 구명보트가 버틸 수 있는 최대 무게 int limit 가 주어진다.

구명보트를 사용해 모든 사람을 옮기기 위해 최소 몇 번을 태워야 하는지 반환해야한다.

 

1. limit은 사람들 중 가장 무거운 사람보다 항상 크게 주어지기 때문에 모두 옮기는 것은 불가능한 경우는 없다.

2. 보트에는 한 번에 2명 까지만 탈 수 있다. (무게가 남더라도)

 

 

 

풀이 .

import java.util.*;

class Solution {
    public int solution(int[] people, int limit) {
        int answer = 0;
        int min = 0;

        Arrays.sort(people);

        // 몸무게가 가장 무거운 사람부터 판단한다.
        for(int max = people.length - 1; min <= max; max--) {
            if(people[max] + people[min] > limit)  // 가장 작은 수와 함께 탈 수 없다면 가장 큰 수는 반드시 혼자 타야만 한다
                answer ++;  // 가장 큰 수 먼저 보낸다. min은 그대로 유지하면서 max만 변경한다
            else {
                answer ++;  // 둘을 같이 보낸다
                min ++;
            }
        }

        return answer;
    }
}

 

처음에 생각한 방식은 위 코드와 달랐다.

 

max 부터 검사를 실시하는 건 동일했지만 min과의 짝은 맨 마지막에 검사했다.

(max, max-1), (max, max-2), ... , (max, min) 순으로 검사했다는 이야기.

 

이유는, 가장 좋은 카드인 min을 처음부터 써버리는 것 보다는.. 무거운 쌍부터 검사해보고 전부 안 될 경우에서야 (max, min) 을 태워 보내는 것이 효율적일 거라고 생각했다. 

(가장 좋은 카드인 min을 최대한 아껴놨다는 느낌? min이 나중에 더 요긴하게 사용될 수도 있으니까)

 

하지만 이런식으로 짰더니 시간초과가 났다.

 

O(n^2) 이고 n=50,000 이기 때문에 가능할 거라고 생각했는데 아니었다.

(50,000 = 5 * 10^4 = 25 * 10^8 = 25 * 1억 이고 25를 상수로 날리면 1억은 1초에 들어오니까. 근데 이게 틀린 계산이었다. 식은 전개하기 전에 n 앞에 상수가 붙어있을때나 날릴 수 있는거지 전부 전개하고 나서 내 마음대로 25를 날려버린 게 틀렸다.)

 

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

 

위 코드는 O(n) 으로 무사히 통과가 가능하다.

 

처음부터 (max, min) 조합을 검사하면 min을 너무 빨리 낭비하는 것 아니냐는 처음의 생각은 틀렸다.

 

max 보다 큰 수는 없기 때문에 min 을 남겨둔다고 해도 더 유용하게 쓰일 때는 오지 않는다.

(max, min) <= limit 이라면 ( ?, min ) 에서 ? 자리에 max 가 아닌 다른 어떤 것이 오더라도 전부 ( ?, min ) <= limit 이다.

따라서 max와 min을 바로 같이 보내버려도 정답에 영향을 미치지는 않는다는 것.

 

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

[PG] 정수 삼각형 JAVA  (0) 2021.01.03
[PG] N으로 표현 JAVA  (0) 2021.01.03
[PG] 큰 수 만들기 JAVA  (0) 2021.01.03
[PG] 조이스틱 JAVA  (0) 2021.01.02
[PG] 체육복 JAVA  (0) 2021.01.02

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

 

코딩테스트 연습 - 큰 수 만들기

 

programmers.co.kr

숫자형 문자로 이뤄진 String number 과 int k가 주어진다.

number 에서 숫자를 k개 제거했을 때 나올 수 있는 수의 최대값을 String으로 반환해야한다.

(단, number 안에서 유지되는 숫자들의 순서가 바뀌면 안 된다. number의 수들로 가장 큰 조합을 찾아내는 게 아니라는 뜻)

 

 

 

풀이 1.

class Solution {
    public String solution(String number, int k) {
        // StringBuilder 안 쓰면 시간초과
        StringBuilder sb = new StringBuilder();

        // j의 범위에 1씩 증가하는 i를 더하는 이유는 뽑아야 하는 숫자가 number.length()-k개에서 하나씩 줄어들기 때문
        // 예를들어 length : 7, k : 3 이라면 새로 뽑아야 하는 수는 총 4개
        // 즉, 늦어도 [k] 에서는 첫번째 수를 뽑아야 하나는 것
        // 이는 다시말하면 뒤에서부터 3개(length - k - 1)는 남겨놓고 나머지 범위에서 하나를 뽑아야 한다는 것
        // 숫자를 하나씩 뽑을 때마다 뒤에 남겨놔야 하는 수의 개수도 하나씩 줄어든다.
        // 그래서 1씩 증가하는 i를 더해준다

        int next = 0;
        for(int i = 0; i < number.length() - k; i++) {
            char max = '0';
            for(int j = next; j <= k + i; j++) {
                if(max < number.charAt(j)) {
                    max = number.charAt(j);
                    next = j + 1;  // 다음 자리수를 선택할 범위의 시작점은 이전 자리수를 선택한 바로 다음으로 잡는다
                }
            }
            sb.append(max);
        }
        return sb.toString();
    }
}

 

혼자서 답을 생각하지 못하고 다른이의 코드를 참고했다.  ( O(n^2) 의 시간 복잡도 )

 

k개의 숫자를 제거해야한다. 다시말해 number.length - k 개의 숫자를 뽑아야 한다.

 

각 자리의 숫자를 어디에서 뽑을지 범위를 특정할 수 있다.

(특정 가능한 범위에서 그때마다 가장 큰 수를 뽑으면 되기 때문에 그리디)

 

첫번째 자리수를 뽑으려면 일단 뒤에서부터 number.length - k - 1 개는 남겨두어야 한다. 그렇지 않으면 남아있는 모든 수를 다 뽑아도 number.length - k 개를 채우지 못할 수도 있기 때문.

 

이를 풀어서 다시 말하면 첫번째 자리 수는 반드시 [0] ~ [k] 에서 나와야 한다.

이 범위에서 가장 큰 수를 골라 첫번째 자리수로 삼는다.

 

두번째 자리는 첫번째 자리를 뽑은 바로 다음 인덱스부터 탐색해야한다.(int next) 이번에는 뒤에서부터 number - k - 2 개를 남겨두어야 한다. (뽑은 숫자가 늘어날 때마다 뒤에서부터 남겨둬야 하는 숫자의 개수는 줄어든다)

 

즉, i 번째 수를 뽑는 반복 for ( int i ) 를 한 번 돌 때마다 그 안에서 실질적으로 i 번째 수를 찾는 역할을 하는 for ( int j )의 범위에 +i 를 해주어야 한다.

 

다시 말하면 탐색 범위의 끝 인덱스는 [k], [k+1], [k+2] ... 이렇게 늘어난다는 이야기.

 

 

 

풀이 2.

import java.util.Stack;

class Solution {
    public String solution(String number, int k) {
        char[] result = new char[number.length() - k];
        Stack<Character> stack = new Stack<>();

        for (int i=0; i<number.length(); i++) {
            char c = number.charAt(i);
            while (!stack.isEmpty() && stack.peek() < c && k-- > 0) {
                stack.pop();
            }
            stack.push(c);
        }
        for (int i=0; i<result.length; i++) {
            result[i] = stack.get(i);
        }
        return new String(result);
    }
}

 

'다른 사람의 풀이' 에서 보고 배운 코드이다.  ( O(n) 의 시간 복잡도 )

 

문자열을 반복을 돌면서 전부 스택에 집어넣는다. 

이때 새로 들어오는 문자가 가장 최근에 들어온 문자보다 크다면 최근에 들어온 문자는 빼버린다.

그 후 스택에서 필요한만큼의 문자를 꺼내 반환한다.

 

O(n^2) 에서 O(n) 으로 줄어들었다.

 

대체 어떻게 이런 생각을 하는거지..?

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

[PG] N으로 표현 JAVA  (0) 2021.01.03
[PG] 구명보트 JAVA  (0) 2021.01.03
[PG] 조이스틱 JAVA  (0) 2021.01.02
[PG] 체육복 JAVA  (0) 2021.01.02
[PG] 카펫 JAVA  (0) 2021.01.01

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

 

코딩테스트 연습 - 조이스틱

조이스틱으로 알파벳 이름을 완성하세요. 맨 처음엔 A로만 이루어져 있습니다. ex) 완성해야 하는 이름이 세 글자면 AAA, 네 글자면 AAAA 조이스틱을 각 방향으로 움직이면 아래와 같습니다. ▲ - 다

programmers.co.kr

완성해야할 이름 String name이 주어진다.

A를 name.length()만큼 이어붙인 문자열에서 name을 만들기 위해 필요한 최소 이동값을 반환해야한다.

 

1. 최초 커서는 첫 번째 칸에 위치한다. 좌 or 우 한 번의 이동으로 옆 칸으로 이동이 가능하다.

2. 커서가 위치한 문자를 위 or 아래 이동을 통해 변경할 수 있다. (ex : A위 -> B, A아래 -> Z)

 

 

 

풀이 1. (틀린 코드지만 정답처리됨)

import java.util.*;

class Solution {
    public int solution(String name) {
        int answer = 0;

        // 문제의 설명은 AAA -> JAN으로 만들어야 하는 것이지만
        // 코드는 JAN -> AAA으로 만드는 식으로 짰다
        ArrayList<Boolean> check = new ArrayList<Boolean>(Arrays.asList(new Boolean[name.length()]));
        Collections.fill(check, Boolean.FALSE);
        for(int i = 0; i < check.size(); i++) {
            if(name.charAt(i) == 'A') {  // A인 것은 true, 나머지는 전부 false로 봤다
                check.set(i, true);
            }
        }

        int cursor = 0;  // 커서를 옮겨가며 진행
        while(check.contains(false)) {
            // 커서가 위치한 곳이 A가 아니라면 A로 만들어주면서 카운트
            if(check.get(cursor) == false) {
                int upCnt = name.charAt(cursor) - 'A';
                int downCnt = 25 - upCnt + 1;
                if(upCnt < downCnt) {  // 위, 아래 중 최소경로를 적용
                    answer += upCnt;
                }else {
                    answer += downCnt;
                }
                check.set(cursor, true);  // A로 만들어줌
            }

            // 커서를 왼쪽, 오른쪽 중 어디로 이동할 것인가?
            int left = cursor - 1, leftCnt = 1;
            int right = cursor + 1, rightCnt = 1;
            while(check.contains(false)) {  // A가 아닌 문자를 찾을 때까지 좌우 번갈아가며 검사
                // 인덱스 범위 벗어나는 이동에 대한 처리
                if(left == -1) {
                    left = name.length() - 1;
                }
                if(right == name.length()) {
                    right = 0;
                }

                // 양 옆의 문자를 한칸씩 확인하여 A가 아닌 것이 있다면 그쪽으로 이동
                // 한칸씩 번갈아가며 화인한다고 하더라도 그 한 칸을 왼,오 어느쪽부터 볼 것인지를 선택해야한다
                // 어떻게 선택해야할지 모르겠어서 그냥 오른쪽 먼저 검사하는 것으로 했다
                // ex : (B)(Cursor)(B) 일 때 오른쪽을 우선으로 이동
                if(check.get(right) == false) {
                    cursor = right;
                    answer += rightCnt;
                    break;
                }

                if(check.get(left) == false) {
                    cursor = left;
                    answer += leftCnt;
                    break;
                }

                left--; leftCnt++;
                right++; rightCnt++;
            }
        }

        return answer;
    }
}

(문제의 설명은 AAA -> JAN으로 만들어야 하는 것이지만 코드는 JAN -> AAA으로 만드는 식으로 짰다)

 

커서를 하나 두고 A가 아닌 곳을 찾아 이동해가며 문자를 변경했다.

 

커서가 위치한 곳에서 왼쪽, 오른쪽 방향으로 A가 아닌 문자가 가장 가깝게 위치한 곳을 다음 커서로 삼았다.

 

이때 커서 양 옆에 동일한 간격으로 A가 아닌 문자가 존재할 때 문제가 발생한다.

왼쪽, 오른쪽 어느 방향에 우선순위를 둬야할지 기준이 모호한 것이다.

잘 모르겠어서 그냥 에라 모르겠다 하고 오른쪽에 우선순위를 두고 풀었다.

 

정답처리가 되긴 했지만 결과적으로 틀린 코드이다. 프로그래머스의 테스트케이스가 부실해서 틀린 코드도 정답처리가 되어버렸다. 

 

(반례)

ABBAAAAAAAAAB 정답 7 -> 8 나온다

BABAAAAAAAAAAAABAAAB 정답 13 -> 15 나온다

 

 

 

풀이 2. (정답 코드)

import java.util.*;

class Solution {
    public int solution(String name) {
        int answer = 0;
        ArrayList<Integer> ansList = new ArrayList<>();
        ArrayList<Character> cList = new ArrayList<>();
        for (int i = 0; i < name.length(); i++) {
            cList.add(name.charAt(i));
        }


        // 일단 위아래 이동부터
        int upDown = 0;
        for (int i = 0; i < cList.size(); i++) {
            char ch = cList.get(i);
            int upCnt = ch - 'A';
            int downCnt = 26 - upCnt;
            upDown += Math.min(upCnt, downCnt);
        }


        // 쭉 오른쪽
        int rightAns = 0, lastRightIndex = 0;
        for (int i = cList.size() - 1; i >= 0; i--) {
            if (cList.get(i) != 'A') {
                lastRightIndex = i;
                break;
            }
        }
        rightAns = lastRightIndex;
        ansList.add(upDown + rightAns);


        // 쭉 왼쪽
        int leftAns = 0, lastLeftIndex = 0;
        for (int i = 1; i < cList.size(); i++) {
            if (cList.get(i) != 'A') {
                lastLeftIndex = i;
                break;
            }
        }
        leftAns = cList.size() - lastLeftIndex;
        ansList.add(upDown + leftAns);


        // 오른쪽 가다가 왼쪽
        lastLeftIndex = 0;  // i보다 큰 인덱스 중에서 가장 왼쪽에 있는 A가 아닌 인덱스
        for (int i = 1; i < cList.size(); i++) {  // i : 방향전환점
            int leftRight = i * 2;  // i까지 이동했다 다시 [0]으로 돌아옴
            for (int j = i + 1; j < cList.size(); j++) { // lastLeftIndex 찾기
                if (cList.get(j) != 'A') {
                    lastLeftIndex = j;
                    break;
                }
            }
            leftRight += cList.size() - lastLeftIndex;
            ansList.add(upDown + leftRight);
        }


        // 왼쪽 가다가 오른쪽
        lastRightIndex = 0;
        for (int i = 1; i < cList.size(); i++) {
            int leftRight = (cList.size() - i) * 2;  // i까지 이동했다 다시 [0]으로 돌아옴
            for (int j = i - 1; j >= 1; j--) {
                if (cList.get(j) != 'A') {
                    lastRightIndex = j;
                    break;
                }
            }
            leftRight += lastRightIndex;
            ansList.add(upDown + leftRight);
        }

        answer = Collections.min(ansList);
        return answer;
    }
}

(문제의 설명은 AAA -> JAN으로 만들어야 하는 것이지만 코드는 JAN -> AAA으로 만드는 식으로 짰다)

 

일단 좌우 이동을 신경쓰기 전에 문자를 변경하는 데 사용되는 위아래 이동부터 한꺼번에 먼저 체크했다.

 

그 이후 좌우 이동을 체크했는데, 이동 가능한 모든 경로에 대한 이동수를 저장하여 그 중 최소값으로 정답을 찾아냈다.

 

아래와 같은 네 가지 경로로 이동이 가능하다.

1. 처음부터 쭉 오른쪽으로만 가기

2. 처음부터 쭉 왼쪽으로만 가기

3. 처음엔 오른쪽으로 가다가 중간에 꺾어서 왼쪽으로 가기 

4. 처음엔 왼쪽으로 가다가 중간에 꺾어서 오른쪽으로 가기

 

1, 2번 경우의 이동 수는 그냥 인덱스를 계산하여 쉽게 구할 수 있다.

 

문제는 3, 4번 경우인데, "중간에 꺾어서"의 중간점을 어디로 잡아야 하는지였다.

 

이것도 그냥 다 해봤다. ( [1] ~ [size()-1] 각각의 점을 꺾는 포인트로 잡고 모든 경우를 계산했다.)

주어지는 문자열의 최대 길이가 20밖에 안 되기 때문에 이 방법을 사용할 수 있었다.

 

 

시간을 너무 많이 들였다... 이렇게까지 시간을 들여서 푸는 게 의미가 있나 싶다.

솔직히 코드도 막무가내 구현 식으로 엉망인 것 같다;

 

 

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

[PG] 구명보트 JAVA  (0) 2021.01.03
[PG] 큰 수 만들기 JAVA  (0) 2021.01.03
[PG] 체육복 JAVA  (0) 2021.01.02
[PG] 카펫 JAVA  (0) 2021.01.01
[PG] 소수 찾기 JAVA  (0) 2021.01.01

+ Recent posts