우선 VIP를 고려하지 않고 규칙을 유추해봅시다.
N=1명일 때 1
만 가능합니다.
N=2명일 때 12
,21
이 가능합니다.
N=3명일 때 123
,132
,213
이 가능합니다. 231
은 불가능합니다.
N=4명일 때 1234
,1243
,1324
,2134
,2143
이 가능합니다. 1342
는 불가능합니다.
그대로 앉는다
, 앞사람과 자리를 바꾼다
두 가지 선택이 가능합니다.1342
는 132
에서 4
를 추가한 뒤 '앞사람과 자리를 바꾼다'
에 해당합니다. 해당 경우가 안되는 이유는 2
가 규칙을 이탈하기 때문입니다.'앞사람과 자리를 바꾼다'
는 조건은 N-1번째 숫자가 N-1일때만 가능합니다.'그대로 앉는다'
는 N-1번째 숫자와 무관하게 가능합니다.(1324
)그래서 N명의 사람이 앉는 경우의 수 = N-1명의 사람이 앉는 경우의 수 + N-1명의 사람이 앉는 경우의 수 중에서 N-1번째 사람이 N-1번째 자리에 앉는 경우의 수
라는 점화식이 성립합니다.
합계
만 따로 살펴봤을 때 피보나치
규칙에 해당한다는 걸 알 수 있습니다.VIP를 고려하면 어떻게 될까요.
123
,132
,213
)에서 뒤에 4만 추가합니다.(1234
,1324
,2134
)1234
,1324
,2134
)에서 뒤에 5만 추가해야 합니다.(12345
,13245
,21345
)저는 i번째 직전이 VIP였는지 판별하기 위해 token이라는 변수를 사용했습니다. i-1번이 VIP라면 token을 true로 둡니다. 해당 token은 i번을 검사할 때 활용되며 다시 false로 초기화됩니다. VIP거나 token이 true면 앞이랑 동일한 값을, 그렇지 않으면 피보나치로 값을 갱신해 줬습니다.
import java.util.*;
import java.io.*;
import java.math.*;
public class Main {
public static void main(String[] args) throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
int M = Integer.parseInt(br.readLine());
int[] arr = new int[M];
for (int i = 0; i < M; i++) {
arr[i] = Integer.parseInt(br.readLine());
}
// 초기값
long[] dp = new long[Math.max(3,N+1)];
dp[0] = 1L;
dp[1] = 1L;
boolean token = false;
if(isContains(arr,1)) { // 예외적으로 두자리숫자는 앞에 값(첫번째 값)이 VIP일때도 영향을 받음
dp[2] = 1L;
}else if(isContains(arr,2)) {
dp[2] = dp[1];
token = true;
}else {
dp[2] = dp[0] + dp[1];
}
// 시작
for (int i = 3; i <= N; i++) {
if(isContains(arr,i)) { // i번째가 VIP면
token = true;
dp[i] = dp[i-1]; // 경우의 수는 증가하지 않는다
}else {
if(token) { // token이다 -> i-1번째가 VIP였다
dp[i] = dp[i-1];
}else {
dp[i] = dp[i-1]+dp[i-2];
}
token = false; // 토큰 초기화
}
}
System.out.println(dp[N]);
}
public static boolean isContains(int[] arr, int x) {
for (int i = 0; i < arr.length; i++) {
if(arr[i] == x) return true;
}
return false;
}
}