[백준] [C/C++] [★] 15990번 : 1,2,3 더하기 5
업데이트:
문제
정수 4를 1, 2, 3의 합으로 나타내는 방법은 총 3가지가 있다. 합을 나타낼 때는 수를 1개 이상 사용해야 한다. 단, 같은 수를 두 번 이상 연속해서 사용하면 안 된다.
- 1+2+1
- 1+3
- 3+1
정수 n이 주어졌을 때, n을 1, 2, 3의 합으로 나타내는 방법의 수를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고, 정수 n이 주어진다. n은 양수이며 100,000보다 작거나 같다.
출력
각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 1,000,000,009로 나눈 나머지를 출력한다.
예제 입력 1
3
4
7
10
예제 출력 1
3
9
27
알고리즘 [ 접근 방법 ]
다이나믹 프로그래밍 문제이다. 간단해 보이지만 같은 수를 두번 이상 연속해서 사용하면 안된다는 조건때문에 까다로운 문제이다.
위의 조건이 없다면 문제의 점화식은 다음과 같다. d[N] = 정수 N을 1,2,3의 합으로 나타내는 방법의 수 d[N] = d[N-1]+d[N-2]+d[N-3] 여기에 같은 수를 두번 이상 연속해서 사용하면 안된다는 조건을 어떻게 붙일까?
연속, 증가, 감소라는 조건이 나왔을 때는 연속한 두개의 수를 살펴보면 된다.
만약 마지막에 더한 수가 +1이라면 그 앞에 올 수 있는 수는 +2,+3일 것이고, 마지막에 더한 수가 +2이라면 그 앞에 올 수 있는 수는 +1,+3, 마지막에 더한 수가 +3이라면 그 앞에 올 수 있는 수는 +1,+2일 것이다.
이를 점화식으로 나타내면, d[N][j] = 정수 N을 1,2,3,의 합으로 나타내는 방법의수, 마지막 수가 j인 경우
이를 코드로 구현하면 된다.
사실 이를 생각해내는것은 그다지 어렵지 않았지만, 실제 이를 코드로 구현해낼때 어떤방식으로 구현해야 할지 헷갈려 풀지 못하고 답안을 살펴봤다. DP문제를 좀 더 풀어보고 나중에 다시한번 풀어봐야 할 것 같다.
풀이
#include <iostream>
using namespace std;
long long int d[1000001][4]; // N이 주어졌을때 1,2,3의 합으로 나타나는 방법의 수, j= 맨뒤에 나온 수
const long long mod = 1000000009LL;
const int limit = 100000;
int main() {
// cin의 연산시간을 scanf만큼 줄여주기 위한 코드 2줄
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int test;
cin >> test;
int num;
for (int i = 1; i <= limit; i++) {
if (i - 1 >= 0) {
d[i][1] = d[i - 1][2] + d[i - 1][3];
if (i == 1) d[i][1] = 1;
}
if (i - 2 >= 0) {
d[i][2] = d[i - 2][1] + d[i - 2][3];
if (i == 2) d[i][2] = 1;
}
if (i - 3 >= 0) {
d[i][3] = d[i - 3][1] + d[i - 3][2];
if (i == 3) d[i][3] = 1;
}
d[i][1] %= mod;
d[i][2] %= mod;
d[i][3] %= mod;
}
while (test--) {
cin >> num;
long long int res = (d[num][1] + d[num][2] + d[num][3]) % mod;
cout << res << endl;
}
}
댓글남기기