https://www.acmicpc.net/problem/1309
1309번: 동물원
첫째 줄에 우리의 크기 N(1≤N≤100,000)이 주어진다.
www.acmicpc.net
코드설명
DP 문제입니다.
처음 문제를 봤을때 첫번쨰 칸과 두번째 칸에 각각 나눠서 사자를 배치하는 경우는 생각할 수 있었으나,
아예 두 칸에 배치하지 않는경우를 DP[N][0] 으로 표현한다는 생각을 못했었습니다.
DP를 아래와 같이 정의했습니다.
dp = new long[N+1][3]; // N번쨰 행에서 [N][0] : 사자를 배치하지 않는경우, [N][1]:첫번째 칸에 사자를 배치하는경우, [N][2]:두번째 칸에 사자를 배치하는경우 dp[1][0] = 1; //사자를 배치하지 않는경우 dp[1][1] = 1; //첫번쨰 줄의 첫번쨰 칸에 사자를 배치하지 않는경우 dp[1][2] = 1; //첫번째 줄의 두번쨰 칸에 사자를 배치하지 않는경우
코드
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Stack; import java.util.StringTokenizer; public class Main { public static int N; public static long[][] dp; public static int answer; public static int MOD = 9901; public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); dp = new long[N+1][3]; // N번쨰 행에서 [N][0] : 사자를 배치하지 않는경우, [N][1]:첫번째 칸에 사자를 배치하는경우, [N][2]:두번째 칸에 사자를 배치하는경우 dp[1][0] = 1; //사자를 배치하지 않는경우 dp[1][1] = 1; //첫번쨰 줄의 첫번쨰 칸에 사자를 배치하지 않는경우 dp[1][2] = 1; //첫번째 줄의 두번쨰 칸에 사자를 배치하지 않는경우 for(int i=2; i<N+1;i++) { dp[i][0] = ( dp[i-1][0] + dp[i-1][1] + dp[i-1][2] ) % MOD; dp[i][1] = ( dp[i-1][0] + dp[i-1][2] )% MOD; dp[i][2] = ( dp[i-1][0] + dp[i-1][1] ) % MOD; } System.out.println( (dp[N][0] + dp[N][1] + dp[N][2] ) % MOD); } }
재귀와 메모이제이션을 활용합니다.
유의해야할점은 완료된 경우를 Coutning 할때는 경우가 1가지이기에 now가 마무리에 도착하면 1가지 경우로 Counting 합니다.
if(now == N-1) return 1;
또 이 문제가 왜 메모이제이션을 적용할 수 있을까요? 바로 최적 부분 구조가 성립되기 떄문입니다.
now 위치에서 다음 번에 check ( 0이면 XX, 1이면 OX, 혹은 XO )로 갈경우의 가지수를 반환한다. 라는 함수를 정의하여 사용합니다.
package Main; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.StringTokenizer; public class Main { static int N, M, S, P, K, A, B, X, L, R; static int answer = 0; static char[][] arr = new char[101][101]; public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); // 10^5 * 4 * 2 = 128MB = 10^7 * 28 N = Integer.parseInt(st.nextToken()); for(int i=0;i<N; i++) { Arrays.fill(cache[i], -1); } answer = DFS(-1, 0) % MOD; System.out.println(answer ); } static int[][] cache = new int[100001][2]; static int MOD = 9901; static int DFS(int now, int check) { if(now == N-1) return 1; int ret = 0; if(now == -1) { ret = (DFS(now + 1, 0) + DFS(now + 1, 1) + DFS(now + 1, 1)) % MOD; return ret; } if(cache[now][check] != -1) return cache[now][check]; if(check == 1) { ret = (DFS(now + 1, 1) + DFS(now + 1, 0) % MOD); }else if(check == 0){ ret += (DFS(now + 1, 1) + DFS(now + 1, 1) + DFS(now + 1, 0)) % MOD; } return cache[now][check] = ret; } }
처음에 실패했던 코드입니다.
어떤 것이 문제였을까요? 바로 for문으로 다음 칸으로 넘어간다는 점입니다.
for문으로 다음칸으로 넘어간다면, 그 빈 공간 사이는 아무것도 하지않고 넘어가는 것이지요.
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import java.util.StringTokenizer; public class Main { static int N, M, S, P, K, A, B, X, L, R; static int answer = 0; static char[][] arr = new char[101][101]; public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); for(int i=0;i<N; i++) { Arrays.fill(cache[i], -1); } answer = DFS(-1, false) % MOD; System.out.println(answer ); } static int[][] cache = new int[100001][2]; static int MOD = 9901; static int DFS(int now, boolean check) { if(now == N-1) return 1; int ret = 0; if(now == -1) { ret = (DFS(now + 1, false) + DFS(now + 1, true) + DFS(now + 1, true)) % MOD; return ret; } if(cache[now][check == true ? 1 : 0] != -1) return cache[now][check == true ? 1: 0]; // 10^5 * 4 * 2 = 128MB = 10^7 * 28 for(int i=1; i<= N - now; i++) { if(i == 1) if(check == true) { ret = (DFS(now + i, true) + DFS(now + i, false) % MOD); }else if(check == false){ ret += (DFS(now + i, true) + DFS(now + i, true) + DFS(now + i, false)) % MOD; } else { ret += (DFS(now + i, true) + DFS(now + i, true) + DFS(now + i, false)) % MOD; } } return cache[now][check == true ? 1 : 0] = ret; } }
'알고리즘 > 백준' 카테고리의 다른 글
[백준] 11722 가장 긴 감소하는 부분 수열 - 동적계획법(Dynamic Programming, DP) + LDS(Longest Decreasing Subsequence) JAVA (0) | 2023.09.15 |
---|---|
[백준] 11057 오르막 수 - DP JAVA (0) | 2023.09.15 |
[백준] 15988 1, 2, 3 더하기 3 - 동적계획법(Dynamic Programming, DP) JAVA (0) | 2023.09.14 |
[백준] 1699 제곱수의 합- 동적계획법(DP, Dynamic Programming) JAVA (0) | 2023.09.14 |
[백준] 14002 가장 긴 증가하는 부분 수열 4 - DP + Stack + LIS JAVA (0) | 2023.09.13 |