카테고리 없음

BOJ 2482: 색상환

wifilion 2024. 10. 7. 16:44

DP문제이다. 원형으로 구성된 문제는 상당히 골치아픈 것이, 맨 위와 맨 아래에 대한 중복체크를

해 주어야 한다. 그러나, "3차원 배열"이라는 한 단어에서 힌트를 얻어 풀이를 작성하면 정말 쉽게 풀 수 있다. 

 

dp문제는 언제나 문제를 dp식으로 모델링하는 것이 문제이다.

dp[i][j][k]를, i번째 원소까지, j개를 선택, 이 때 k=0: 현재를 선택하지 않음 / k=1: 현재를 선택함

으로 모델링하면 된다. 

이제, 포함 / 비포함으로 문제를 풀면 중복제거도 할 필요 없이 자연스레 문제가 풀린다.

첫 번째 식에서, dp[1][0][0]=0; dp[1][1][1]=1;은 1번째 원소를 반드시 선택하겠다는 뜻이 된다.

이 때에는 sum에 dp[N][K][0]만을 더해주면 된다.

다음 식에서, dp[1][0][0]=1; dp[1][1][1]=0;은 1번째 원소를 반드시 선택하지 않겠다는 뜻이 된다.

이 때에는 sum에 dp[N][K][1], dp[N][K][1] 모두 더해 주면 된다. 

즉, 한마디로 말하자면, "애초에 선택하는 경우에 대한 초기 경우의 수를 0으로 두기" / "애초에 선택하지 않는 경우에 대한 초기 경우의 수를 0으로 두기"라는 두 가지 갈래에 대한 덧셈만 구하면 그만인 것이다.

 

dp식은 너무 쉽게도,

dp[i][j][0]=dp[i-1][j][0]+dp[i-1][j][1]

dp[i][j][1]=dp[i-1][j-1][0]이다.

왜 그런지는 스스로 생각해 보라. 물론 모듈러 덧셈이기에 INF를 어디서 나누던 상관은 없다. 필자는 이런 것을 생각하는 걸 별로 좋아하지 않기 때문에(사실 귀찮기 때문에) 매번 나누었다. 당연히 그래도 문제는 없다.

 

#include<bits/stdc++.h>
#define INF 1000000003
using namespace std;
typedef long long int ll;
ll N, K, dp[1001][1001][2], sum;
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> N >> K;
	dp[1][0][0]=0; dp[1][1][1]=1;
	for(int i=2;i<=N;i++){
		for(int j=0;j<=K;j++){
			if(j==0){
				dp[i][j][0]=dp[i-1][j][0]; 
				continue;
			}
			dp[i][j][0]=dp[i-1][j][0]+dp[i-1][j][1];
			dp[i][j][0]%=INF;
			dp[i][j][1]=dp[i-1][j-1][0];
			dp[i][j][1]%=INF;
		} 
	}
	sum+=dp[N][K][0];
	sum%=INF;
	for(int i=0;i<1001;i++){
		for(int j=0;j<1001;j++){
			dp[i][j][0]=0;
			dp[i][j][1]=0;
		}
	}
	dp[1][0][0]=1; dp[1][1][1]=0; // 1번째 선택 X
	for(int i=2;i<=N;i++){
		for(int j=0;j<=K;j++){
			if(j==0){
				dp[i][j][0]=dp[i-1][j][0]; 
				continue;
			}
			dp[i][j][0]=dp[i-1][j][0]+dp[i-1][j][1];
			dp[i][j][0]%=INF;
			dp[i][j][1]=dp[i-1][j-1][0];
			dp[i][j][1]%=INF;
		} 
	}
	sum+=dp[N][K][1];
	sum%=INF;
	sum+=dp[N][K][0];
	sum%=INF; // modular 연산에 대한 덧셈 법칙 
	cout << sum;
}