티스토리 뷰
https://www.acmicpc.net/problem/12852
12852번: 1로 만들기 2
첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 자연수 N이 주어진다.
www.acmicpc.net
2가지 방법을 가져왔다. 첫 번째 방법과 두 번째 방법은 해결한 방식의 차이가 크다. 천천히 살펴보자.
1. 1부터 시작하여 N까지의 최소값을 구하기.
1부터 시작해서 N까지 가는데, 이렇게 하는 것이다.
1로 갈 수 있는 숫자 -> 3, 2 -> 2와 3의 최소 횟수 갱신과 동시에 1을 저장.
2로 갈 수 있는 숫자 -> 2, 4, 6 -> 2, 4, 6의 최소 횟수 갱신이 가능할 경우 갱신 (최소 경로 값과 2)
3으로 갈 수 있는 숫자 -> 4, 6, 9 -> 최소 횟수 갱신이 가능할 경우 갱신
1을 접근했을 경우
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
0 | 1 | 1 | 1e9 | 1e9 | 1e9 | 1e9 | 1e9 | 1e9 | 1e9 |
0 | 1 | 1 |
2로 접근했을 경우
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
0 | 1 | 1 | dp[2][0] + 1 = 2 | 1e9 | dp[2][0] + 1 = 2 | 1e9 | 1e9 | 1e9 | 1e9 |
0 | 1 | 1 | 2로 접근 | 2로 접근 |
3을 접근했을 경우
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
0 | 1 | 1 | 2 | 1e9 | 2 | 1e9 | 1e9 | dp[3][0] + 1 = 2 | 1e9 |
0 | 1 | 1 | 2 | 2 | 3으로 접근 |
이런식으로 1 ~ N까지 접근하면 dp[N][0]의 값이 최소 경로가 될 것이고, dp[N][1]을 따라 가다보면 그 경로를 알 수 있게 된다.
코드는 다음과 같다.
#include <cstdio>
int dp[1000001][2];
int main(void) {
int N; scanf("%d", &N);
int idx = N;
dp[1][0] = 0;
for (int i = 2; i <= N; i++) dp[i][0] = 1e9;
for (int i = 1; i < N; i++) {
if (i * 3 <= N && dp[i * 3][0] > dp[i][0] + 1) dp[i * 3][1] = i, dp[i * 3][0] = dp[i][0] + 1;
if (i * 2 <= N && dp[i * 2][0] > dp[i][0] + 1) dp[i * 2][1] = i, dp[i * 2][0] = dp[i][0] + 1;
if (dp[i + 1][0] > dp[i][0] + 1) dp[i + 1][1] = i, dp[i + 1][0] = dp[i][0] + 1;
}
printf("%d\n", dp[N][0]);
while (idx != 1) {
printf("%d ", idx);
idx = dp[idx][1];
}
printf("1");
}
하지만 이 방식은 dp[1000001][2]만큼의 공간이 필요하다. 그래서, 이러한 공간이 필요 없는 방법이 존재하는데, 그 방법이 2번 방법이다.
2. dfs와 map을 사용한 dp
dfs를 활용하여 1을 만드는 최소 갯수는 다음 글을 통해 알 수 있다.
https://jhcard.tistory.com/139
[S3] 1463. 1로 만들기
https://www.acmicpc.net/problem/1463 1463번: 1로 만들기 첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다. www.acmicpc.net 나는 dp로 해결하였다. 하지만,, 엄청난 방법으로 해결하는 코드
jhcard.tistory.com
이 글에 나오는 dfs함수를 참고하고, n1 < n2일 경우 map[num] = num / 3을 저장하고, 이외에는 map[num] = num / 2를 저장해준다. 그러면 dfs(N)으로 최소 값을 알 수 있게 되고, map을 되돌아가다보면 그 경로를 출력할 수 있게 되는 것이다.
코드는 다음과 같다.
#include <cstdio>
#include <map>
std::map<int, int> m;
int dfs(int n) {
if (n <= 1) return 0;
int a = dfs(n / 3) + n % 3;
int b = dfs(n / 2) + n % 2;
if (a < b) {
m[n] = n / 3;
return a + 1;
}
else {
m[n] = n / 2;
return b + 1;
}
}
int main(void) {
int N; scanf("%d", &N);
printf("%d\n", dfs(N));
while (N > 1) {
int next = m[N];
printf("%d ", N);
while (N != 2 * next && N != 3 * next) printf("%d ", --N);
N = next;
}
printf("1");
}
'코딩테스트 > Silver 1' 카테고리의 다른 글
[S1] 5014. 스타트링크 (0) | 2022.12.27 |
---|---|
[S1] 1325. 효율적인 해킹 (1) | 2022.12.26 |
[S1] 1850. 최대공약수 (0) | 2022.12.23 |
[S1] 1495. 기타리스트 (0) | 2022.12.23 |
[S1] 16194. 카드 구매하기 2 (0) | 2022.12.23 |
- Total
- Today
- Yesterday
- 비트마스킹
- 확장 유클리드
- Witcher3
- tsp알고리즘
- portal1
- scanf
- writeString
- 해시맵
- 플로이드-워셜
- 분할정복
- Set
- list
- 좌표 압축 알고리즘
- readInt
- 큰 수 계산
- deque와 vector의 차이
- unistd.h
- 행렬 멱법
- cin.tie(nullptr);
- 피보나치
- bits/stdc++.h
- manber myers
- SCC 알고리즘
- fread
- writeInt
- ios::sync_with_stdio(false)
- 트리보나치
- readString
- fastIo
- 에라토스테네스의 체
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |