Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[동적 계획법] 4월 5일 #7

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions [동적계획법] 4월 5일/11727_sample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include <iostream>
#include <vector>

using namespace std;
const int MOD = 10007; // 상수 MOD = 10007, 10007로 나눈 나머지를 출력하기 위함

// fillTile(n) 함수
int fillTile(int n) { // n: 정수, 2*n
vector<int> dp(n + 1, 0); // vector dp, 크기는 n+1, 0으로 초기화

if (n == 1) { // 인덱스 에러 안나도록 n이 1일 경우 바로 리턴
return 1; // n=1이면 리턴 1
}

// 너비 1, 2인 타일 채우는 경우의 수 미리 초기화
dp[1] = 1; // n=1 -> 1
dp[2] = 3; // n=2 -> 3

// n>= 3인 경우
for (int i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + 2 * dp[i - 2]; // 연산
dp[i] %= MOD; // 구하는 과정에서 int 범위 초과할 수 있으므로 마지막에 한 번이 아니라 중간 중간 모듈러 연산을 해줘야 함
}
return dp[n]; // 리턴 dp[n]
}

/**
* 너비를 인덱스로 써서 n까지의 너비를 채우는 방법의 수를 저장하자!
*
* 우선, 너비 1은 2(높이)x1(너비) 타일로 채우는 경우밖에 없음
* 너비 2는 1x2 2개와 2x2 1개 총 2 경우 + 너비 1에 2x1 타일을 더한 1 경우 -> 3 경우
*
* 그 후, 너비 3부터는 각각 너비 1, 2만큼을 뺀 타일에서 1, 2 너비 타일을 각각 더하는 경우를 생각해주자
* 이때, 중복 경우의 수가 생기지 않도록 너비 2의 경우에서 1에서 더한 경우는 빼줌
* -> dp[n] = (너비 1인 타일 채우는 경우의 수 = 1) * dp[n - 1]
* + (너비 2인 타일 채우는 경우의 수 = 2) * dp[n - 2]
*
* -> dp[n] = 1 * dp[n - 1] + 2 * dp[n - 2] (n >= 3)
*
* !주의! 모듈러 연산 해야함
*/

int main() {
int n; // 정수 n, 2*n의 n

// 입력
cin >> n; // 입력 받은 정수를 n에 저장

// 연산 & 출력
cout << fillTile(n); // fillTile(n) 함수 호출
return 0;
}
46 changes: 46 additions & 0 deletions [동적계획법] 4월 5일/9084_sample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include <iostream>
#include <vector>

using namespace std;

// kanpsack() 함수
int knapsack(int n, int m, vector<int> &coin) { // n: 동전의 가지 수, m: 만들 금액, coin: 동전 각 금액
vector<int> dp(m + 1, 0); // vector dp(주어진 동전을 이용하여 조건 금액을 만드는 경우의 수), 크기는 m+1, 0으로 초기화

dp[0] = 1; // 0원을 만드는 경우의 수 1

for (int i = 0; i < n; i++) {
for (int j = coin[i]; j <= m; j++) { // j: 동전 종류를 나타내기 위함, 만들어야 하는 금액인 m까지 반복
dp[j] += dp[j - coin[i]]; // 해당 동전을 사용하기 전 경우의 수와 현재 경우의 수를 더함
}
}
return dp[m]; // m원을 만드는 경우의 수 리턴
}

/**
* dp[i] = 주어진 동전 종류를 사용해서 i원을 만드는 경우의 수
*
* dp[0] = 1 을 넣고 시작 (0원을 만드는 경우의 수 1로 생각)
* 각 동전마다 해당 동전부터 만들어야 하는 금액(m)까지 돌리면서 해당 동전을 사용하기 전 금액의 경우의 수와 현재 경우의 수를 더함
* 해당 동전 사용하기 전 금액의 경우의 수가 0이면 금액을 만들 수 없는 경우이지만, 어차피 더해도 값 변화는 없으므로 따로 고려하지 않음
*/

int main() {
int t, n, m; // t: 테스트케이스 개수, n: 동전의 가지 수, m: n가지 동전으로 만들어야 할 금액

// 입력
cin >> t; // 테스트케이스 저장
while (t--) { // 테스트케이스 개수 동안
cin >> n; // 동전의 가지 수 n 저장
vector<int> coin(n, 0); // vector coin(n가지 동전의 각 금액을 저장), 크기는 n, 0으로 초기화
// n가지 동전의 각 금액 저장 (오름차순 배열)
for (int i = 0; i < n; i++) {
cin >> coin[i]; // 금액 저장
}
cin >> m; // 만들어야 할 금액 m 저장

// 연산 & 출력
cout << knapsack(n, m, coin) << '\n'; // knapsack() 함수 호출
}
return 0;
}
49 changes: 49 additions & 0 deletions [동적계획법] 4월 5일/9095_sample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include <iostream>
#include <vector>

using namespace std;
const int MAX = 10; // 상수 10, 정수 n의 max 값

// bottom-up 방식 dp 배열 채우기
// n=1 -> 1가지, n=2 -> 2가지 따로 작성
vector<int> numberOfAllCases() { // 함수 numberOfAllCases(), 벡터 반환
vector<int> dp(MAX + 1, 0); // vector dp, 크기는 11(MAX+1), 0으로 초기화

dp[0] = dp[1] = 1; // 인덱스가 0(더미 인덱스), 1인 dp(n=1인 경우)는 1
dp[2] = 2; // 인덱스가 2인 dp(n=2인 경우)는 2로
// n>= 3인 경우
for (int i = 3; i <= MAX; i++) {
dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]; // 찾은 규칙 연산, 0으로 초기화 되어 있던 dp 값 업데이트
}
return dp; // dp 리턴
}

/**
* [bottom-up 접근]
* 각 수를 인덱스로 써서 정수 n까지 1, 2, 3의 합으로 나타내는 방법의 수를 저장하자!
*
* 우선 3까지 1, 2, 3의 합으로 나타내는 방법의 수 초기화 함
* 해당 인덱스에서 -1, -2, -3 한 인덱스에 +1, +2, +3을 해줬다고 생각하면 됨
*
* -> dp[n] = dp[n - 1] + dp[n - 2] + dp[n - 3] (n >= 3)
*
* 해당 풀이는 인덱스 관리를 편하게 하기 위해 0을 더미 인덱스로 줘서 인덱스 3부터 연산
* 테스트케이스로 입력이 들어오고, 입력 범위가 11로 작기 때문에 미리 dp 배열 채우고 시작하는 것이 더 효율적
*/

int main() {
int t, n; // t: 테스트 케이스의 개수, n: 정수

// 미리 dp 채우기
vector<int> dp = numberOfAllCases(); // 함수 numberOfAllCases() 호출, 리턴 값 vector dp에 저장

// 입력
cin >> t; // 입력 받은 테스트케이스 수 저장
while (t--) { // t개 동안
cin >> n; // 입력 받은 정수 값 n에 저장

// 출력
cout << dp[n] << '\n'; // 인덱스 n에 해당하는 dp 값 출력
}
return 0;
}