[BOJ][DP][브1][2839] 설탕 배달
문제 링크
첫 번째 풀이 : 완전탐색
Warning Notice: 두 번째 풀이가 더 나은 풀이입니다. 두 번째 풀이 이후 첫 번째 풀이의 문제점을 제시합니다.
알고리즘
- 5킬로그램 봉지를 최대 N개 가져갈 수 있다고 할 때, 5킬로그램 봉지를 0~N개 가져가는 경우에 대해 2.를 진행한다.
- 남은 설탕이 3킬로그램 봉지로 나누어 떨어지는지 확인한다.
- 1.과 2.로 얻은 정답이 최소인지 매번 판단해서 저장한다.
정답 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <algorithm>
using namespace std;
int main(void) {
int n, n_copy;
int answer = 987654321;
int ans_three, ans_five;
cin >> n;
for (int ans_five = 0; ans_five <= n / 5; ans_five++) {
int rest = n - ans_five * 5;
if (rest % 3 == 0) {
answer = min(answer, ans_five + rest / 3);
}
}
if (answer != 987654321) {
cout << answer;
}
else {
cout << -1;
}
return 0;
}
두 번째 풀이 : 완전탐색
알고리즘
- 5킬로그램 봉지로 나누어 떨어지는지 확인한다.
- 나누어 떨어지지 않으면 3킬로그램 봉지를 하나만 채워본다.
- 다시 5킬로그램 봉지로 나누어 떨어지는지 확인한다.
- 5킬로그램으로 나누어 떨어지지 않으면 계속 3킬로그램이 감소할 것이고, 마지막에는 0, -1, -2킬로그램이 남게 된다. 0이 남은 경우 3킬로그램으로 나누어 떨어진 경우, 아니라면 나누어 담지 못하는 경우이다.
정답 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <algorithm>
using namespace std;
int main(void) {
int n;
int ans_three = 0;
cin >> n;
while (n > 0 && n%5 != 0) {
n -= 3;
ans_three++;
}
if (n < 0) {
cout << -1;
}
else {
cout << n / 5 + ans_three;
}
return 0;
}
세 번째 풀이 : DP
알고리즘
마지막에 더해진 봉지는 5g 또는 3g입니다.
dp[i][0] = 마지막에 더해진 봉지가 5g인 경우 dp[i][1] = 마지막에 더해진 봉지가 3g인 경우
i가 3~7일 때는 직접 계산해서 저장해두고, 나누어 떨어지는 경우 방문처리를 합니다. 방문처리는 ig을 나누어서 담을 수 있다는 뜻입니다.
정답 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int MAX = 5010;
const int MOD = 10007;
int arr[MAX][MAX];
ll dp[MAX][2];
bool visited[MAX];
int dx[4] = { 0,0,1,-1 };
int dy[4] = { 1,-1,0,0 };
int n, m;
void show(void);
void bfs(int x, int y);
int main()
{
//freopen("input.txt", "r", stdin);
ios_base::sync_with_stdio(0); cin.tie(0);
cin >> n;
dp[3][1] = 1; visited[3] = true;
dp[5][0] = 1; visited[5] = true;
dp[6][1] = 2; visited[6] = true;
for (int i = 8; i <= n; i++) {
if (visited[i - 3]) {
dp[i][0] = dp[i - 3][0];
dp[i][1] = dp[i - 3][1] + 1;
visited[i] = true;
}
if ((dp[i][0] + dp[i][1]) < (dp[i - 5][0] + 1 + dp[i - 3][1])) {
dp[i][0] = dp[i - 5][0] + 1;
dp[i][1] = dp[i - 5][1];
visited[i] = true;
}
}
if (visited[n])
cout << dp[n][0] + dp[n][1];
else cout << -1;
return 0;
}
결론
무게가 작은 봉지로 나누어 떨어지는지 확인하는 방법을 사용하면(첫 번째), 두 번째 풀이와 비슷한 풀이같지만 코드가 불필요하게 복잡해지는 문제점이 있을 수 있습니다.
1
2
3
4
5
for (int ans_five = 5; ans_five >=0 ; ans_five--) {
if( (n - ans_five * 5) % 3 == 0){
//pass
}
}
Success Notice: 첫 번째 풀이의 문제점은 더 작은 킬로그람으로 나누어 떨어지는지 확인하는데에 있습니다. 무게가 큰 봉지로 나누어 떨어지는지 확인하는것이 더 좋은 방법입니다.
Leave a comment