백준 9095번 - 1, 2, 3 더하기

황제연·2024년 9월 13일
0

알고리즘

목록 보기
101/169
post-thumbnail

문제 탐색하기

입력 자료 정리

  1. 첫번째 입력값은 이후 들어올 입력값의 수인 테스트케이스 수이다
  2. 이후 들어오는 테스트케이스 수만큼의 입력값들은
    몇번째 인덱스에 있는 dp 배열의 값을 출력할지에 대한 값이다

해결방법 추론

  1. 이 문제의 풀이는 두가지를 생각할 수 있다.
    가장 쉬운 첫번째 방법은 4를 나타내는 방법은 7가지가 있다는 문제를 보고,
    1, 2, 3일때는 어떤 경우의 수를 가지며 규칙이 무엇이 있을까 고민해서 나온 규칙으로 푸는
    DP 방법이 있을 것이다
  2. 이어서 두번째 방법은 최대 n이 11보다 작다는 점과,
    선택할 수 있는 숫자도 1,2,3으로 3가지라는 점을 이용하여 백트래킹으로 푸는 방법이다
    놀랍게도 백트래킹으로 풀어도 시간제한이나 메모리제한 없이 문제가 해결된다
  3. 두가지 방법 모두로 풀어보았다. 먼저 DP로 푼 방법부터 설명하겠다

점화식 추론

  1. 점화식을 추론해낸 과정은 다음과 같다.
    4를 1,2,3으로 나타내는 방법이 7가지 있으니까 1,2,3 각각을 나타내는 방법은 몇가지 있을까
    고민하고 찾아봤다
  2. 1,2,3은 각각 1, 2, 4가지 경우가 있었다. 이때 이 3가지를 더하면 4를 나타내는 방법이 된다
  3. 그렇다면 점화식은 dp[i] = dp[i-3] + dp[i-2] + dp[i-1]이 되겠는데,
    과연 이 점화식이 맞을까라는 의문을 품었다
  4. 5와 6으로 검증을 하기 위해서는 내가 직접 경우의 수를 구해야한다.
    하지만 경우의 수를 구하는 것에서 오류가 발생할 수 있고, 따라서 예제 입력에서 주어진
    7을 통해 검증하기로 하였다
  5. 위 점화식에 의하면 5와 6은 간단히 구할 수 있다.
    5는 2 + 4 + 7으로 13이 되며, 6은 4 + 7 + 13으로 24가 된다
  6. 그렇다면 7은 어떻게 될까? 7 + 13 + 24가 되며, 합하면 44가 된다.
    예제 출력의 결과와 일치한다.
  7. 따라서 점화식은 위에 나온 식이 맞으며 해당 방법으로 문제를 풀면 해결 될 것이다

DP 풀이 시간복잡도 계산

  1. 테스트케이스는 최대 입력값을 주지 않았기 때문에 시간복잡도에서 제외하면,
    최대 입력값인 10이 최대 연산이 될 것이다.
  2. 그리고 이것은 배열에 넣는 과정에서만 발생하므로 시간복잡도는 상수 시간이 발생한다
  3. 따라서 DP 풀이에서의 시간복잡도는 O(1)이 되며, 시간 제한안에 문제를 해결할 수 있다

백트래킹 해결방법 고민

  1. 백트래킹으로도 문제를 해결할 수 있다. n의 입력값이 매우 작기때문에 가능하다
  2. 진행해온 경로를 백트래킹 파라미터로 누적해서 관리하며,
    합산으로 base condition역할을 한다면 쉽게 구할 수 있을 것이다
  3. 이때 선택한 경로가 중복되는 경우를 제외하기 위해 Set 자료구조를 사용한다면
    더 편하게 문제를 해결할 수 있다

백트래킹 풀이 시간복잡도 계산

  1. 백트래킹 풀이의 시간복잡도는 테스트케이스마다 백트래킹을 실행하므로
    테스트케이스만큼의 시간복잡도가 발생함을 고려하기는 해야한다
  2. 하지만 테스트케이스의 최대 입력값이 안 주어졌기 때문에
    문제를 해결할 수 있을 만큼의 테스트케이스를 줄 것이므로 크게 고려하지 않아도 된다
  3. 백트래킹의 시간복잡도만 생각하면 된다. 매 함수실행마다 3가지 경우를 선택한다
  4. 이때, 최악의 경우 1만 선택되어서 n번만큼 실행되는 경우이므로 3^n만큼의 연산이 발생할 것이다
  5. 따라서 시간복잡도는 O(3^n)일 것이고 최대 입력값 10을 생각했을 때,
    시간제한안에 문제를 해결할 수 있다

코드 설계하기

DP값 상태 관리하기

  1. DP의 값은 미리 1번 인덱스부터 10번 인덱스까지 구해서 int형 1차원 배열로 관리한다
  2. 이때 1,2,3은 미리 1,2,4로 초기화하고 4번 인덱스부터는 점화식으로 값을 구해 관리한다

테스트케이스 출력 설계

  1. 테스트케이스는 T라는 이름의 int형 변수로 관리한다
  2. 이후 들어오는 입력값은 dp의 인덱스 값이며, 이 인덱스에 해당되는 값을 출력하면 정답이 된다

backtracking 설계

  1. 1,2,3 선택을 편하게 하기 위한 int형 1차원 배열을 하나 선언하고 원소값으로 1,2,3을 관리한다
  2. 또한 중복 제거를 위한 Set을 하나 만든다.
    String 타입으로 선택 경로를 관리하며, 매 입력마다 Set을 초기화하여 이전 결과의 영향을 받지 않도록 한다
  3. 백트래킹의 base condition은 sum이 n보다 크거나 같은 경우이며,
    이때 n과 같은 경우 set에다가 진행 경로인 String 값을 추가한다
  4. select 배열의 크기인 3만큼 순회하면서 재귀식을 실행하고,
    선택된 값의 원소를 sum에 더하며, 문자열로 파싱해서 경로에다가 추가한다

backtracking 결과 출력

  1. 완성된 Set의 크기를 출력하면 정답이 된다

정답 코드

(dp 풀이, 1회차 시도 성공!)

import java.io.*;
import java.util.*;


public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int T = Integer.parseInt(br.readLine());
        int[] dp = new int[11];
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 4;
        for (int i = 4; i < 11; i++) {
            dp[i] = dp[i-3] + dp[i-2] + dp[i-1];
        }
        for (int i = 0; i < T; i++) {
            int n = Integer.parseInt(br.readLine());
            bw.write(dp[n]+"\n");
        }

        

        br.close();
        bw.close();
    }
}

(백트래킹 풀이, 1회차 시도 성공!)

import java.io.*;
import java.util.*;


public class Main {

    static int[] select = {1,2,3};
    static Set<String> set;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int T = Integer.parseInt(br.readLine());
        for (int i = 0; i < T; i++) {
            int n = Integer.parseInt(br.readLine());
            set = new HashSet<>();
            backtracking(n,0,"");
            bw.write(set.size()+"\n");
        }

        br.close();
        bw.close();
    }

    private static void backtracking(int n, int sum, String ans) {
        if(sum >= n){
            if(sum == n){
                set.add(ans);
            }
            return;
        }


        for (int i = 0; i < 3; i++) {
            backtracking(n,sum+select[i], ans + String.valueOf(select[i]));
        }

    }
}

문제 링크

9095번 - 1, 2, 3 더하기

profile
Software Developer

0개의 댓글