PS/BOJ2022. 4. 28. 00:14

암호코드 (https://www.acmicpc.net/problem/2011)

 

기본점화식은 아래와 같다.

dp[i] = dp[i - 1] + dp[i - 2];

무슨 의미인지 한참을 생각했다.

 

ex) 알파벳은 1~26 사이의 숫자로 구성되며 나머지 숫자는 성립되지 않으므로 모두 예외적인 상황이다.

경우의 수 계산을 쉽게하기 위해 25114가 아닌 25134를 사용한다.

 

25134
답 : 6 
0 1   
1 1 (2)  : 2밖에 없으니 표현할 수 있는 경우는 1.
2 2 (25) : 알파벳이 성립함. 2,5 / 25  여기서 dp[0]=1로 초기화 하지않으면  해당 값에 대한 계산이 불가. 
3 2 (51) : 26을 초과하므로 2,5 / 25 뒤에 1을 붙이는 경우만 존재.  단순하게 붙이는 경우엔

 경우의 수 변경 없음.


4 4 (13) : 기본 점화식으로 계산

6 (14) : 기본 점화식 기본 점화식 계산

 

dp 초기화를 1로 해야하는 이유는 위의 점화식의 d[i-2] 계산 때문이다.

dp[0] = 1;
dp[1] = 1;

 

피보나치 수열에 조건이 많은 경우처럼 보인다.

 

이해를 위해 2513 까지만 따져보면

 

1단계

2 -> 2를 배치하는 경우 1가지

2단계

2,5 / 25 -> 2 2가지

3단계 (알파벳 성립 안하면 경우의 수 변화는 없다.)

2,5,1 / 251 -> 2가지

4단계

2, 5,1,1 / 25, 1 ,1 (1을 연달아 붙인 경우 d[i-1]와 같다)

2,5,11 / 25, 11 (1두개를 합한 경우 d[i-2]와 같다.)

여기 까지만 보면 1, 11 => 2가지 경우를 d[i-1]*2 로 구했는지 dp[i - 1] + dp[i - 2] 로 구했는지 알 수가 없다.

사실 이부분 이해하는데 시간이 엄청 걸린거 같다.

 

제대로 따져보기 위해 5단계를 거치면.

 

5단계

2,5,1,1,3  / 2,5,11,3 / 25, 1 ,1,3/ 25, 11, 3 (1을 연달아 붙인 경우 d[i-1]와 같다)

자 그렇다면 두개를 조합한 경우는 왜 d[i-2]가 되는가?

조합한 경우를 만드려면 이전 단계에 11로 된 수에서 1을 찢어와야한다.

위의 경우에서 맨 뒷자리를 모두 13으로 만들도록 구성을해보면 아래와 같다.

2, 5,  1, 13 / 2, 5 1, 13 (앞의 11에서 1을 뺏어옴) / 25, 1, 13 / 25,1,13(이하동문) => 중복을 제외한 2가지 경우

 

자, 만들고나니 중복되는 경우의 수가 보인다.

 

현재 만든 경우의 수에서 중복 2개를 제거하고, 조합된 수의 고정된 1,13을 지우고나면?

2, 1 / 25, 1 만 남게된다. 여기서 2단계의 경우의 수가 보이면 된 것이다.

 

따라서 4단계 경우의 수 = 3단계 경우의 수 + 2단계 경우의 수 라는 것이 이해가 돼야한다.

( d[i-1]*2 가 성립하지 않는 반례가되며,  단순히 d[i-1] + d[i-2] 가 된다는 점화식을 파악하는 것을 넘어 각 요소가 무엇을 의미하는지 알게 됨)

 

예외처리가 계속 틀려 멘붕이었는데 아래 글에서의 예제를 활용해 검증하니 AC되었다.

 

반례로 사용한 입력은 아래 글에서 참조.

 

출처 : https://www.acmicpc.net/board/view/44917

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

	static long[] dp;
	static long MOD = 1000000L;

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		String s = st.nextToken();
		
		dp = new long[5001];
		int N = s.length();
		if( s.charAt(0)=='0') {
			System.out.println("0");
			return;
		}
		dp[0] = 1;
		dp[1] = 1;

		//first : i-2 , current : i-1
		for (int i = 2; i <= s.length(); ++i) {
			if (s.charAt(i-1)=='0') {				
				if( s.charAt(i-2) == '1' || s.charAt(i-2) == '2' ) { // 10 or 20
					dp[i] = dp[i - 2] % MOD;
				} else { // 30,40,50~ 모두 성립하지 않음.
					dp[i] = 0;
				}				
			} else { //0으로 시작하지 않는 경우.				
				int val = Integer.parseInt(s.subSequence(i - 2, i).toString());
				//System.out.println("val : "+ val);
				if (val >= 1 && val <= 9) {//0으로 시작한다는것? 앞자리가 20 or 30이라는 의미. 1203 
					dp[i] = dp[i - 1] % MOD;
				}
				else if (val >= 11 && val <= 26) {
					dp[i] = dp[i - 1] + dp[i - 2] % MOD;
				}else {
					dp[i] = dp[i - 1] % MOD;
				}
				
			}
			
		}

		System.out.println(dp[N] % MOD);
/*		
 		for (int i = 0; i <= N; ++i)
			System.out.printf("%d %d\n", i, dp[i]);
		System.out.println();
*/
	}
}

'PS > BOJ' 카테고리의 다른 글

1107  (0) 2022.04.29
1476  (0) 2022.04.28
11052  (0) 2022.04.27
2225  (0) 2022.04.27
2133  (0) 2022.04.26
Posted by easy16