[백준] 1767 N-Rook II
import java.util.*;
import java.io.*;
public class Main{
// 차례대로 n, m, k 일 때 가능한 경우의 수를 담는 변수
static long[][][] d;
static final int mod = 1000001;
static long go(int n, int m, int k){
// 만일 이 중 하나라도 음수라면 애초에 불가능한 짓을 한거니까 0 반환
if( n < 0 || m < 0 || k < 0 ){
return 0;
}
// 위에 안걸리면서 룩을 모두 소진한거라면 성공!
if( k == 0 ){
return 1;
}
// 메모된 값이 있다면 그걸 쓰자
if(d[n][m][k] != -1){
return d[n][m][k];
}
// 모든 경우의 수를 담을 변수
long ans = 0;
// 1. n번째 행에 룩을 놓지 않는 경우
// n-1까지 진행된 경우의 수를 그대로 계승한다
ans += go(n-1, m, k);
// 2. n번째 행에 룩을 하나 놓는데, 아무에게도 공격받지 않는 경우
// n-1까지 진행된 경우 중에서 m-1열까지만 있는 경우를 가져온다
// 왜냐하면 적어도 한 열은 비어있어야 아무에게도 공격받지 않기 때문
// m개의 열 중 하나를 고르는것과 같으므로 mC1을 곱해준다
ans += go(n-1, m-1, k-1)*m;
// 3. n번째 행에 룩을 두개 놓는 경우
// 이미 서로가 공격하고 있는 상황이므로
// 놓인 열에서는 아무에게도 공격받으면 안된다.
// 즉 n-1까지 진행된 경우중에서 m-2열까지만 있는 경우르 가져온다
// 그래야 적어도 2열은 깨끗 하자너...
// 대신 mC2를 곱해야 한다
ans += go(n-1, m-2, k-2)*m*(m-1)/2;
// 4. n번째 행에 룩을 하나 놓는데,
// 이미 기존에 놓여진 룩에 의해 세로로 공격 받는 경우
// 이건 사실 달리 말하면 행을 두개 잡아서 같은 열에 두개를 놓는거랑 같음
// 따라서 n-2까지 진행된걸 가져오되 n-1C1 을 곱해줘야함
// 마찬가지로 열 중에서 하나를 골라서 놓을거니까 mC1도 곱해줘야함
ans += go(n-2, m-1, k-2)*m*(n-1);
// 결과를 mod로 나누고 메모이제이션 해준다
ans %= mod;
d[n][m][k] = ans;
return ans;
}
public static void main(String []args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
int m = Integer.parseInt(br.readLine());
int k = Integer.parseInt(br.readLine());
d = new long[n+1][m+1][k+1];
for(int i = 0 ; i <= n ; i++){
for(int j = 0 ; j <= m ; j++ ){
Arrays.fill(d[i][j], -1);
}
}
System.out.println(go(n, m, k));
}
}
다이나믹적인 사고가 꽉 막힌 느낌이다 ㅠ
고딩때했던 귀납법이었나 그건 그냥 n = 1 일때 증명하고, n = k 일때 n = k+1 을 만족한다면 모든 n에 대해 해당 식이 성립한다.. 뭐 그런거 였는데(bottom up) top down은 반대 방식이라 익숙하지고 않고, 사고하기가 힘들다 .
Comments