https://www.acmicpc.net/problem/18427
#include <iostream>
#include <vector>
#define DIV 10007
using namespace std;
vector<int> block[51];
//dp[i][j]: i명의 학생이 j 높이만큼 쌓는 경우의 수
int dp[51][1001];
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int n, m, h;
cin >> n >> m >> h;
cin.ignore(1);
for (int i = 1; i <= n; i++) {
string s;
getline(cin, s); //줄 단위로 입력받기
int temp = 0;
for (int j = 0; j < s.size(); j++) {
if (s[j] == ' ') {
block[i].push_back(temp);
temp = 0;
} else {
temp = temp * 10 + (s[j] - '0');
}
}
if (temp != 0) {
block[i].push_back(temp);
}
}
for (int i = 0; i <= n; i++) {
dp[i][0] = 1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=h;j++){
for(int k=0;k<block[i].size();k++){
if(j-block[i][k]>=0){
dp[i][j]+=dp[i-1][j-block[i][k]];
dp[i][j]%=10007;
}
}
dp[i][j]+=dp[i-1][j];
dp[i][j]%=10007;
}
}
cout << dp[n][h];
return 0;
}
1. 문제
1번부터 N번까지의 학생들은 각각 블록들을 가지고 있다. 학생마다 최대 M개의 블록을 가지고 있을 수 있으며, 한 명의 학생이 가지고 있는 모든 블록들의 높이는 서로 다르다. 이 때 1번부터 N번까지의 학생들이 가진 블록을 차례대로 사용하여 바닥에서부터 쌓아올려 하나의 탑을 만들고자 한다.
단, 어떤 학생의 블록은 사용하지 않아도 되며 한 학생당 최대 1개의 블록만을 사용할 수 있다.
1번부터 N번까지의 학생들이 가지고 있는 블록들에 대한 정보가 주어졌을 때, 높이가 정확히 H인 탑을 만들 수 있는 경우의 수를 계산하는 프로그램을 작성하시오.
예를 들어 N=3, M=3, H=5일 때, 각 학생마다 가지고 있는 블록들의 높이가 다음과 같다고 가정하자.
- 1번 학생: 2, 3, 5
- 2번 학생: 3, 5
- 3번 학생: 1, 2, 3
이 때, 탑의 높이가 정확히 5가 되도록 블록을 쌓는 경우로는 다음의 6가지가 존재한다. (블록을 사용하지 않는 경우는 X로 표시하였다.)

입력
첫째 줄에 자연수 N, M, H가 공백을 기준으로 구분되어 주어진다. (1 ≤ N ≤ 50, 1 ≤ M ≤ 10, 1 ≤ H ≤ 1,000) 둘째 줄부터 N개의 줄에 걸쳐서 각 학생이 가진 블록들의 높이가 공백을 기준으로 구분되어 주어진다.
단, 모든 블록의 높이는 1,000 이하의 자연수이며 한 명의 학생이 가지고 있는 모든 블록들의 높이는 서로 다르게 주어진다.
출력
첫째 줄에 높이가 H인 탑을 만드는 경우의 수를 10,007로 나눈 나머지를 출력한다.
2. 풀이
cin >> n >> m >> h;
cin.ignore(1);
for (int i = 1; i <= n; i++) {
string s;
getline(cin, s); //줄 단위로 입력받기
int temp = 0;
for (int j = 0; j < s.size(); j++) {
if (s[j] == ' ') {
block[i].push_back(temp);
temp = 0;
} else {
temp = temp * 10 + (s[j] - '0');
}
}
if (temp != 0) {
block[i].push_back(temp);
}
}
입력 파트이다. 각 학생이 가지고 있는 블록의 높이는 string 으로 받아 잘라서 저장한다.
//dp[i][j]: i명의 학생이 j 높이만큼 쌓는 경우의 수
int dp[51][1001];
dp를 위와 같이 정의한다. i 명의 학생이 j의 높이까지 쌓는 경우의 수 이다.
for (int i = 0; i <= n; i++) {
dp[i][0] = 1;
}
초기값이다. i 명의 학생이 높이 0을 만드는 경우는 모든 학생이 블록을 쌓지 않는 경우인 1이다.
for(int i=1;i<=n;i++){
for(int j=1;j<=h;j++){
for(int k=0;k<block[i].size();k++){
if(j-block[i][k]>=0){
dp[i][j]+=dp[i-1][j-block[i][k]];
dp[i][j]%=10007;
}
}
dp[i][j]+=dp[i-1][j];
dp[i][j]%=10007;
}
}
i 명의 학생이 높이 j를 맞추는 데, 각 학생은 본인이 가진 k번째의 블록을 사용한다. 그 경우의 수는 다음과 같다.
- i번째 학생이 k번째 블록을 놓는 경우: j-block[i][k]에서 해당 학생이 블록을 놓는 경우이므로 경우의 수가 똑같다. 처음의 높이는 음수가 될 수 없으므로 j>=block[i][k]여야 한다.
- 블록을 놓지 않는 경우: i번째 학생이 관여를 하던 안하던 높이는 j로 같으므로 dp[i-1][j]와 같은 경우의 수를 가진다.
문제는 경우의 수를 10007로 나눈 나머지를 구하는 것이므로 각 연산에 대하여 나머지 연산을 해준다.
연산이 끝나고, n명의 학생이 h를 만드는 경우, 즉 dp[n][h]를 출력하면 정답을 구할 수 있다.
'Coding Test' 카테고리의 다른 글
백준 2758 로또 (C++) (1) | 2024.09.11 |
---|---|
백준 2631 줄세우기 (C++) (0) | 2024.09.09 |
백준 12904 A와 B (C++) (0) | 2024.09.03 |
백준 1790 수 이어 쓰기 2 (C++) (3) | 2024.08.31 |
백준 6198 옥상 정원 꾸미기 (C++) (0) | 2024.08.27 |