Knapsack
09 May 2019
HackerRank link
주어진 배열로 목표 합을 배열의 원소를 0번 또는 여러번 사용하여 넘치지 않고 가장 근접한 합을 만들어 리턴!!
ex) arr=[2,3,4] target sum = 10 인경우
[2,2,2,2,2] , [2,2,3,3], 등..
input
t
n k
배열 원소들
t : test case 수
n : 배열 사이즈
k : 목표 합
제약조건
1 <= t <= 10
1 <= n,k,arr[i] <= 2000
풀이 과정
먼저 심플하게 생각해 보면 모든 경우의 수를 다 해보면 풀릴 것 같다.
또한, 예시 입력을 봤을 때에도 arr[i]의 각 원소는 중복 될 수 있다.
모든 경우의 수를 다 해보면 비 효율적이므로,
첫 번째로 할만한 경우는 더하지 않고 가능한 경우이다.
어떤 수를 나눈 나머지가 0 인경우 더해볼 필요가 없다. 그럼 어떻게 나눠야 할까?
소수 구하는 방법처럼 2, 3의 곱들을 제거 해 보았다.
또한 2+3 = 5의 경우도 만들어질 수 있으므로, 제거해 보았다.
그런데 23같은 경우 2x10+3이므로 23도 지워져야 된다. 따라서, 곱셈만으로는 구하기 어려워 보인다.
이 때문에 효율적으로 모든 경우의 수를 더하여 체크한는 방법을 생각해 보았다.
재귀적으로 현재 수에서 배열의 수를 순차적으로 더하는 방법이 가능하다.
그런데, 제약 조건을 봤을 때, n,k,arr[i]이 2000이다. 사이즈가 굉장히 적다.
사이즈가 굉장히 적기 때문에, flag를 두어서 이미 체크한 경우를 생각할 수있다.
그래서 중복으로 함수를 call 할필요 없게 만들 수 있다.
또한, 입력의 중복된 원소를 제거하기 위해 오름차순으로 정렬을 한다음 중복되는 수는 제거해 준다.
풀이 핵심
- 제약조건이 낮기 때문에, 심플하게 재귀적으로 모든 수를 더하여 체크할 수 있다.
 - 제약조건이 낮기 때문에, flag를 두어 중복 체크를 방지 가능
 
Algorithm
- 입력받은 수를 오름차순으로 정렬한다.
 - 중복된 입력은 제거 한다.
 - 재귀적으로 0부터 입력을 모두 더하여 체크 한다.
 
Source Code
import java.io.*;
import java.math.*;
import java.security.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.regex.*;
public class Solution {
    // Complete the unboundedKnapsack function below.
    static final int  maxInput = 2000;
    static int checkMax = 0;
    
    static void check(boolean[] isChecked, ArrayList<Integer> al, int cur, int target) {
        
        if(cur > target || cur > isChecked.length) return;
        
        if(isChecked[cur]) {
            return;
        }
        
        isChecked[cur] = true;
        
        if(checkMax < cur ) checkMax = cur;
        
        for(int e : al) {
            check(isChecked, al, cur+e, target);
        }
        
    }
    
    static int unboundedKnapsack(int k, int[] arr) {
         ArrayList<Integer> al = new ArrayList<>(); 
         boolean[] isChecked = new boolean[maxInput +1];
         checkMax = 0;
            
         Arrays.sort(arr);
         
         int i = 0;
         int before = 0;
         for(int e : arr) {
             if(before == e) {
                 continue;
             }
             
             al.add(e);
             before = e;
         }
         
         check(isChecked, al, 0,k);
         
         return checkMax;
    }
    private static final Scanner scanner = new Scanner(System.in);
    public static void main(String[] args) throws IOException {
          BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));
        int t = scanner.nextInt();
        scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
        for(int test = 0; test < t; test++) {
            String[] nk = scanner.nextLine().split(" ");
            int n = Integer.parseInt(nk[0]);
            int k = Integer.parseInt(nk[1]);
            int[] arr = new int[n];
            String[] arrItems = scanner.nextLine().split(" ");
            scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
            for (int i = 0; i < n; i++) {
                int arrItem = Integer.parseInt(arrItems[i]);
                arr[i] = arrItem;
            }
            int result = unboundedKnapsack(k, arr);
            System.out.println(result);
            bufferedWriter.write(String.valueOf(result));
            bufferedWriter.newLine();
        }
        
        bufferedWriter.close();
        scanner.close();
    }
}