알고리즘 공부를 하면서, 언젠가는 이해를 하고 구현을 해봐야지 했던 것이 다이나믹 프로그래밍 DP였다.
큰 문제를 작은 문제의 결과값을 이용해서 계속 풀어나가는 알고리즘인데, 대표적인 문제로는 피보나치수열을 DP배열에 저장하면서 푸는 방법이 있다.
가장 중요한 것이, 점화식을 빨리 찾아내는 것이라고 생각한다.
백준 18427번 문제를 풀어보았다.
이 문제는 몇번째 학생의 높이가 h일때의 경우의수를 계속 더해가면서, 내가 구하고자 하는 n번째 학생 h높이일 때 최종적으로 다 더한 경우의 수를 DP배열에서 반환해주면 된다.
이 문제에서는 경우의 수를 더해가는 것이 중요했는데
예제를 기준으로 보면 처음 DP테이블 초기화는 이렇게 했다.
dp[i][0] ~ dp[i][h] 까지 채우면서, i가 N까지 반복을 한다
n \ h | 0 | 1 | 2 | 3 | 4 | 5 |
0 | 1 | 0 | 0 | 0 | 0 | 0 |
1 | 1 | 0 | 0 | 0 | 0 | 0 |
2 | 1 | 0 | 0 | 0 | 0 | 0 |
3 | 1 | 0 | 0 | 0 | 0 | 0 |
높이가 0 일때 1, 2, 3번 학생 모두 높이가 0인 탑을 쌓는 경우는, 고르지 않는 경우가 있기 때문에, 1로 설정한다.
n \ h | 0 | 1 | 2 | 3 | 4 | 5 |
0 | 1 | 0 | 0 | 0 | 0 | 0 |
1 | 1 | 0 | 1 | 1 | 0 | 1 |
2 | 1 | 0 | 0 | 0 | 0 | 0 |
3 | 1 | 0 | 0 | 0 | 0 | 0 |
dp[i][j] 칸에서의 경우는 두가지
1. j에 해당하는 블록을 i번째 학생이 갖고 있을 때 고르는 경우
2. 고르지 않는 경우
1번에 해당하는 경우는
내부에 for을 하나 추가해서, i번째 학생의 가진 블록을 순회하면서,
if(j >= blocks[i][k]) dp[i][j] = (dp[i][j] + dp[i-1][j-blocks[i][k]]) % 10007;
즉 현재 조사하는 높이 j가 현재 보고있는 학생의 가진 블록보다 크거나 같으면, 계산을 수행하는데,
j-blocks[i][k]번째를 dp에서 찾아서 더해주는 이유는, (H - 현재 블록 높이) 까지의 경우의 수를 더해줘야 하기 때문이다.
이후 i번째 학생이가진 모든 블록에 대해 조사가 끝나면,
dp[i][j] = (dp[i][j] + dp[i-1][j]) % 10007;
를 수행해주는데, 이건 아까 말한 2번조건, 고르지 않았을때 dp테이블에서 처리해주는 방식이다.
전체 코드
#include <iostream>
#include <vector>
#include <sstream>
using namespace std;
vector<int> blocks[51];
int dp[51][1001] = {0, };
/*
2 3 5
3 5
1 2 3
dp table(경우의 수)
nh0 1 2 3 4 5
0 1 0 0 0 0 0
1 1 0 1 1 0 1
2 1 0 1 2 0 3
3 1 1 2 4 3 6
*/
int N, M, H;
int main(){
cin >> N >> M >> H;
cin.ignore();
for(int i = 1; i <= N; i++){
string str;
getline(cin, str);
stringstream sstr(str);
int num;
while(sstr >> num) blocks[i].push_back(num);
}
//dp테이블에서 아무것도 선택하지 않았을 경우 즉 높이가 0으로 주어졌을때 선택하지 않으면 되므로 1로 초기화
for(int i = 0; i <= N; i++) dp[i][0] = 1;
//dp테이블에서 1번째 학생부터 검사 시작
for(int i = 1; i <= N; i++){
//dp테이블에서 i번째 학생의 행을 채우기(주어진 높이에 따라서)
for(int j = 1; j <= H; j++){
//학생이 가진 각 블록에 대해 검사
for(int k = 0; k < blocks[i].size(); k++){
//j-blocks[i][k]를 하는 이유 -> (H - 현재 블록 층수)까지의 경우의 수를 더해주는 것
if(j >= blocks[i][k]) dp[i][j] = (dp[i][j] + dp[i-1][j-blocks[i][k]]) % 10007;
}
dp[i][j] = (dp[i][j] + dp[i-1][j]) % 10007;
}
}
cout << dp[N][H] % 10007;
}
'알고리즘' 카테고리의 다른 글
DFS, BFS 알고리즘 (1) (0) | 2023.02.10 |
---|---|
매개변수 탐색 알고리즘(1) (0) | 2023.02.08 |