조합 예제 - 8
문제 분석
완전 순열이라는 개념의 문제이다.
완전 순열의 개념은 n개의 원소의 집합에서 원소들을 재배열할 때 이전과 같은 위치에 배치되는 원소가 1개도 없을 때를 말한다.
이 문제에서 필요한 것은 완전 순열의 개념이 아니라 문제에 주어진 조건에 따라 적절한 점화식을 도출해내는 역량이다.
손으로 풀어보기
dp[N] 배열의 의미를
N명일 때 선물을 교환할 수 있는 모든 경우의 수로 정한다.N명이 존재한다고 가정했을 때, A가 B에게 선물을 줬다고 가정해본다. 이때 교환 방식은 다음 2가지 방식만이 존재한다.선물을 교환하는 2가지 방식
B도 A에게 선물을 줬을 때(양방향)
N명 중 2명이 교환을 완료했으므로 남은 경우의 수는dp[N-2]
B는 A가 아닌 다른 사람에게 선물했을 때(단방향)
N명 중 B만 받은 선물이 정해진 상태이므로 남은 학생은N - 1이며, 경우의 수는dp[N - 1]
이것은 A가 B라는 학생에게 선물을 준 것으로 가정하고 경우의 수를 생각한 것이지만 실제로는 A는 자기 자신을 제외한 N - 1명에게 선물을 전달할 수 있다.
이 내용을 이용해 도출해낸 완전 순열의 경우의 수를 구하는 점화식은 다음과 같다.
완전 순열 점화식
dp[N]=(N - 1) * (dp[N - 2] + dp[N - 1])
도출해낸 점화식으로 이 문제를 해결할 수 있다.
dp 배열을 초기화한다.
dp[1] = 0: 혼자서는 선물을 교환할 수 없다.dp[2] = 1: 두 명인 경우 서로 교환하는 경우만 존재한다.
완전 순열 점화식으로 정답을 구한다.
dp[3]=(3 - 1) * (dp[1] + dp[2])= 2 * 1 = 2dp[4]=(4 - 1) * (dp[2] + dp[3])= 3 * 3 = 9dp[5]=(5 - 1) * (dp[3] + dp[4])= 4 * 11 =44
슈도코드
n(학생 수)
dp(n명일 때 선물을 교환할 수 있는 모든 경우의 수 배열)
dp[1] = 0
dp[2] = 1
for i 3~n:
dp[i] = (i - 1) * (dp[i-1] + dp[i-2]) % 1_000_000_000 # 항상 mod 연산한 값을 저장한다.
dp[n] 출력코드 구현 - 파이썬
n = int(input())
dp = [0] * 1_000_001
dp[1] = 0
dp[2] = 1
for i in range(3, n + 1):
dp[i] = (i - 1) * (dp[i - 1] + dp[i - 2]) % 1_000_000_000
print(dp[n])코드 구현 - 자바
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
long[] dp = new long[1_000_001];
dp[1] = 0;
dp[2] = 1;
for (int i = 3; i <= n; i++) {
dp[i] = (i - 1) * (dp[i - 1] + dp[i - 2]) % 1_000_000_000;
}
System.out.println(dp[n]);
}
}Last updated