알고리즘 풀이

[프로그래머스] (java) 타겟넘버 (BFS, DFS)

Below_zero 2025. 3. 15. 03:58

문제 설명

 

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


-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3

 


사용할 수 있는 숫자가 담긴 배열 numbers, 타겟 넘버 target이 매개변수로 주어질 때 숫자를 적절히 더하고 빼서 타겟 넘버를 만드는 방법의 수를 return 하도록 solution 함수를 작성해주세요.

제한사항


- 주어지는 숫자의 개수는 2개 이상 20개 이하입니다.
- 각 숫자는 1 이상 50 이하인 자연수입니다.
- 타겟 넘버는 1 이상 1000 이하인 자연수입니다.


입출력 예

numbers target return
[1, 1, 1, 1, 1] 3 5
[4, 1, 2, 1] 4 2



입출력 예 #1

문제 예시와 같습니다.

입출력 예 #2


+4+1-2+1 = 4
+4-1+2-1 = 4

 

 

설명

n개의 음이 아닌 정수가 주어지고 target 정수가 주어지는데, 이 n개의 정수들의 합과 차의 여러 조합중에서 target을 만족하는 경우가 몇개인지 세야 한다. 핵심은 각각의 정수가 +, -로 변할 수 있으며 순서는 변하지 않는다는 것이다. 따라서 경우의 수만 따지자면, 모든 정수 합의 경우는 n개의 정수가 +, -로 구분되는 2가지 경우가 있으니 총 2^n개가 나온다.

이를 단순하게 이진 트리로 만들어본다면 다음 그림과 같다.

 

입출력 예의 numbers가 [4,1,2,1]이고 target이 4인 경우, 총 2가지 경우만 있다는 것을 확인할 수 있다

 

즉 트리를 탐색하면 답을 구할 수 있다.

 

첫번째 풀이 : DFS

첫 노드에서부터 시작해 마지막 노드까지 쭉 타고내려가며 깊이 우선 탐색을 하면서 각 노드를 더하거나 빼면 경우가 각각 나오므로 각 경우를 target과 비교하면 된다.

class Solution {
	int count = 0;
    public int solution(int[] numbers, int target) {
        dfs(numbers, target, 0, 0);
        
        return count;
    }

    private int dfs(int[] numbers, int target, int index, int sum) {
        if (index == numbers.length) { // 마지막 leaf까지 진행했을 때 
        	if(sum == target){ 
            	count++;
            }
        }

        // 현재 숫자를 더하는 경우와 빼는 경우 두 가지 탐색
        return dfs(numbers, target, index + 1, sum + numbers[index])
             + dfs(numbers, target, index + 1, sum - numbers[index]);
    }
}

 

 

두번째 풀이 : BFS

 

큐를 이용해 각 level의 노드를 더한결과, 뺀결과를 각각 담아 모든 합의 경우을 구하는 것이 목표이다,.

 

큐는 (현재까지 사용한 숫자의 개수(level), 현재 합)으로 구성된다.

초기 상태는 (0,0)으로 시작하는 것으로 가정한다.

 

알아듣기 쉽도록 각 단계 코드의 흐름을 쪼개어 적어보자면

단계 큐의 상태 설명
0 [(0,0)] 초기 상태
1 [(1,4), (1,-4)] level 1 노드의 4를 더한 결과와 뺀 결과
2 [(2,5), (2,3), (2,-3), (2,-5)] level 2 노드의 1를 더한 결과와 뺀 결과
3 [(3,7), (3,3), (3,5), (3,1), (3,-1), (3,-5), (3,-3), (3,-7)] level 3 노드의 2를 더한 결과와 뺀 결과
4 [(4,8), (4,6), (4,4), (4,2), (4,6), (4,4), (4,2), (4,0), (4,0), (4,-2), (4,-4), (4,-6), (4,-2), (4,-4), (4,-6), (4,-8)] level 4 노드의 1를 더한 결과와 뺀 결과

 

즉 모든 단계를 진행한 후(= 현재까지 사용한 숫자의 개수 == numbers.length)

큐의 현재 합 부분이 target 과 일치하는 경우를 세면 될 것이다.

import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public int solution(int[] numbers, int target) {
        Queue<int[]> queue = new LinkedList<>(); // java에서 queue 선언하기
        queue.offer(new int[]{0, 0}); // {현재 인덱스, 현재 합}
        int count = 0;

        while (!queue.isEmpty()) {
            int[] node = queue.poll();
            int index = node[0];
            int sum = node[1];

            if (index == numbers.length) { // 모든 숫자를 사용한 경우
                if (sum == target) count++;
            } else {
                queue.offer(new int[]{index + 1, sum + numbers[index]}); // 더하는 경우
                queue.offer(new int[]{index + 1, sum - numbers[index]}); // 빼는 경우
            }
        }

        return count;
    }
}

 

DFS 쪽이 연상이나 구현이나 더 간단했던 것 같다.