ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 5557. 1학년
    알고리즘/백준 2022. 9. 25. 21:04
    https://www.acmicpc.net/problem/5557
     

    5557번: 1학년

    상근이가 1학년 때, 덧셈, 뺄셈을 매우 좋아했다. 상근이는 숫자가 줄 지어있는 것을 보기만 하면, 마지막 두 숫자 사이에 '='을 넣고, 나머지 숫자 사이에는 '+' 또는 '-'를 넣어 등식을 만들며 놀

    www.acmicpc.net

    문제

    상근이가 1학년 때, 덧셈, 뺄셈을 매우 좋아했다. 상근이는 숫자가 줄 지어있는 것을 보기만 하면, 마지막 두 숫자 사이에 '='을 넣고, 나머지 숫자 사이에는 '+' 또는 '-'를 넣어 등식을 만들며 놀고 있다. 예를 들어, "8 3 2 4 8 7 2 4 0 8 8"에서 등식 "8+3-2-4+8-7-2-4-0+8=8"을 만들 수 있다.

    상근이는 올바른 등식을 만들려고 한다. 상근이는 아직 학교에서 음수를 배우지 않았고, 20을 넘는 수는 모른다. 따라서, 왼쪽부터 계산할 때, 중간에 나오는 수가 모두 0 이상 20 이하이어야 한다. 예를 들어, "8+3+2-4-8-7+2+4+0+8=8"은 올바른 등식이지만, 8+3+2-4-8-7이 음수이기 때문에, 상근이가 만들 수 없는 등식이다.

    숫자가 주어졌을 때, 상근이가 만들 수 있는 올바른 등식의 수를 구하는 프로그램을 작성하시오.

     

    입출력 조건

    • 첫째 줄에 숫자의 개수 N이 주어진다. (3 ≤ N ≤ 100) 둘째 줄에는 0 이상 9 이하의 정수 N개가 공백으로 구분해 주어진다.
    • 첫째 줄에 상근이가 만들 수 있는 올바른 등식의 개수를 출력한다. 이 값은 2^63-1 이하이다.

     

    LOGIC

    1. 각 정수에 대해 + 또는 -를 선택하는 경우의 수 ⇒ 즉, 부분 집합을 이용한다.
    2. N이 100일경우 경우의 수는 2^98이므로 시간초과를 고려해 DP 사용

     

    이 문제는 + 또는 -를 사용한 부분 집합의 경우의 수에서 조건에 맞는 경우의 수를 구하는 문제이다.

    static long cal(int sum, int idx) {
        if(idx == N-1) { //끝까지 계산한 경우
            return sum == last ? 1 : 0; //값이 맞으면 경우의 수 +1
        }
    
        int s = sum + arr.get(idx); //더할 경우
        int d = sum - arr.get(idx); //뺄 경우
        long cnt = 0; //경우의 수
    
        if(s < 21) {
            cnt += cal(s, idx + 1);
        }
    
        if(idx != 0 && d > -1) {
            cnt += cal(d, idx + 1);
        }
    
        return cnt;
    }

     

    하지만 부분 집합의 경우의 수는 2^n 이므로 시간단축을 위해 DP로 메모이제이션을 사용하여 풀어야한다.

    ...
    //입력된 숫자 중 N-1개의 값들에 대한 기록
    //만들 수 있는 숫자는 0~20이므로 21개의 계산 결과 기록
    dp = new long[N-1][21];
    ...
    
    static long cal(int sum, int idx) {
        if(idx == N-1) { //끝까지 계산한 경우
            return sum == last ? 1 : 0; //값이 맞으면 경우의 수 +1
        }
    
        if(dp[idx][sum] != -1) //이미 했던 경우의 수 일 경우 저장된 값 리턴
            return dp[idx][sum];
    
        int s = sum + arr.get(idx); //더할 경우
        int d = sum - arr.get(idx); //뺄 경우
        long cnt = 0; //경우의 수
    
        if(s < 21) {
            cnt += cal(s, idx + 1);
        }
    
        if(idx != 0 && d > -1) {
            cnt += cal(d, idx + 1);
        }
    
        return dp[idx][sum] = cnt;
    }

     

    위 로직에서 또 주의할 점은 첫 번째 수로 0이 나올 경우 - 연산 후 음수 값이 나오면 로직이 끝나므로 주의해야 한다.

    // idx가 0이 아니고 뺀 경우의 수가 0 이상일 경우에만 진행
    if(idx != 0 && d > -1) {
    	cnt += cal(d, idx + 1);
    }

    CODE

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.StringTokenizer;
    
    /**
     * 백준 5557. 1학년 - 골드 5
     * @category DP
     */
    public class Main {
    	static int N, last;
    	static ArrayList<Integer> arr;
    	static long dp[][]; //메모이제이션
    
    	public static void main(String[] args) throws Exception {
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		StringTokenizer st;
    
    		N = Integer.parseInt(br.readLine());
    		arr = new ArrayList<>();
    		dp = new long[N-1][21];
    		
    		st = new StringTokenizer(br.readLine());
    		for(int i=0; i<N-1; i++) {
    			int n = Integer.parseInt(st.nextToken());
    			
    			arr.add(n);
    			Arrays.fill(dp[i], -1);
    		}
    		
    		last = Integer.parseInt(st.nextToken());
    		cal(0, 0);
    		System.out.println(dp[0][0]);
    	}
    
    	static long cal(int sum, int idx) {
    		if(idx == N-1) { //끝까지 계산한 경우
    			return sum == last ? 1 : 0; //값이 맞으면 경우의 수 +1
    		}
    		
    		if(dp[idx][sum] != -1) //이미 했던 경우의 수 일 경우 저장된 값 리턴
    			return dp[idx][sum];
    		
    		int s = sum + arr.get(idx); //더할 경우
    		int d = sum - arr.get(idx); //뺄 경우
    		long cnt = 0; //경우의 수
    		
    		if(s < 21) {
    			cnt += cal(s, idx + 1);
    		}
    		
    		if(idx != 0 && d > -1) {
    			cnt += cal(d, idx + 1);
    		}
    		
    		return dp[idx][sum] = cnt;
    	}
    }
    

    '알고리즘 > 백준' 카테고리의 다른 글

    9370. 미확인 도착지  (0) 2022.10.06
    2887. 행성 터널  (0) 2022.10.02
    1644. 소수의 연속합  (2) 2022.09.22
    20056. 마법사 상어와 파이어볼  (0) 2022.09.18
    21609. 상어 중학교  (0) 2022.09.18

    댓글

Designed by Tistory.