티스토리 뷰
문제 링크 : https://www.acmicpc.net/problem/12852
12852번: 1로 만들기 2
첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 자연수 N이 주어진다.
www.acmicpc.net
주어진 연산을 이용해서 n 을 1 로 만드는데 필요한 비용(횟수) 외에도 경로도 출력한다는 것을 빼면 평범한 dp 문제.
n 을 무엇으로 줄이면 최적인가를 기록해두는 prv 배열을 도입하면 충분하다.
탑다운 방식과 바텀업 방식을 모두 소개하기 쉬운 문제라 기록해 둔다.
1. Top-Down
dp[n] 은 에 dp[n/3]+1 , dp[n/2]+1 이거나 dp[n-1]+1 중 작은 값이다.
( n 이 2, 3 의 배수가 아니면 앞의 항들은 사용하지 않는다.)
#include<bits/stdc++.h>
using namespace std;
const int Inf=1e9;
int dp[1000001], prv[1000001];
int recur(int n){
if(n==1) return 0;
int &ret=dp[n];
if(ret) return ret;
ret=Inf;
if(n%3==0) {
ret=recur(n/3)+1;
prv[n]=n/3;
}
if(n%2==0) {
int t=recur(n/2)+1;
if(ret>t){
ret=t;
prv[n]=n/2;
}
}
int t=recur(n-1)+1;
if(ret>t){
ret=t;
prv[n]=n-1;
}
return ret;
}
int main(){
int n; scanf("%d",&n);
printf("%d\n", recur(n));
for(int i=n;i!=0;i=prv[i])
printf("%d ",i);
printf("\n");
}
2. Bottom-Up 1
바텀업도 두 가지 방식으로 나누어보자.
탑다운에서 설명한 것과 같이
i 를 줄여나갈 때 더 작은 수 i-1, i/2, i/3 중 무엇을 선택하는 것은 좋은가?
를 그대로 반영한 코드는 다음과 같다.
이 코드는 무늬는 바텀업이지만 i 에서 작은 수를 고려한다.
#include<bits/stdc++.h>
using namespace std;
const int Inf=1e9;
int dp[1000001], prv[1000001];
int main(){
int n; scanf("%d",&n);
for(int i=3;i<=n;i++) dp[i]=Inf;
dp[1]=0, dp[2]=1, prv[2]=1;
for(int i=3;i<=n;i++){
if(dp[i]>dp[i-1]+1){
dp[i]=dp[i-1]+1;
prv[i]=i-1;
}
if(i%2==0 && dp[i]>dp[i/2]+1){
dp[i]=dp[i/2]+1;
prv[i]=i/2;
}
if(i%3==0 && dp[i]>dp[i/3]+1){
dp[i]=dp[i/3]+1;
prv[i]=i/3;
}
}
printf("%d\n", dp[n]);
for(int i=n;i!=0;i=prv[i])
printf("%d ", i);
printf("\n");
}
3. Bottom-Up 2
이번에는 반대로 i 가 더 큰 수를 만드는데 기여하는 바를 정리하자.
1~i 까지 최적의 방법을 알고 있다고 하자.
i 에서 1 을 만드는 최소 연산 횟수를 dp[i] 라 하면 3 가지 방식으로 기여할 수 있다.
dp[i+1] 을 결정하기 위해서는 dp[i]+1 을 고려해야 한다.
dp[2*i] , dp[3*i] 역시 마찬가지다.
이런 사고 방식을 적용한 것이 다음 코드이다.
i 에서 i 보다 큰 수를 들여다 본다는 점에서 바텀업이라 불리는데 이 코드가 더 어울려 보인다.
#include<bits/stdc++.h>
using namespace std;
const int Inf=1e9;
int dp[1000001], prv[1000001];
int main(){
int n; scanf("%d",&n);
for(int i=2;i<=n;i++) dp[i]=Inf;
for(int i=1;i<n;i++){
if(dp[i+1]>dp[i]+1){
dp[i+1]=dp[i]+1;
prv[i+1]=i;
}
if(2*i<=n && dp[2*i]>dp[i]+1) {
dp[2*i]=dp[i]+1;
prv[2*i]=i;
}
if(3*i<=n && dp[3*i]>dp[i]+1) {
dp[3*i]=dp[i]+1;
prv[3*i]=i;
}
}
printf("%d\n", dp[n]);
for(int i=n;i!=0;i=prv[i])
printf("%d ", i);
printf("\n");
}
'DS\Algo > Dynamic Programming' 카테고리의 다른 글
BOJ 7579 앱 (1) | 2022.09.25 |
---|---|
BOJ 2011 암호코드(Alphacode) (1) | 2022.09.24 |
BOJ 17070 파이프 옮기기 1 (2) | 2022.09.19 |
BOJ 10942 팰린드롬? (0) | 2022.09.17 |
BOJ 1915 가장 큰 정사각형 (0) | 2022.09.15 |
- Total
- Today
- Yesterday
- max flow
- script
- Aho-Corasick
- C++ big number
- lazy propagation
- Vim
- math font
- dynamic programming
- Reference
- map
- bash script
- BOJ
- Dijkstra
- 정수론
- persistent segment tree
- 다익스트라
- fenwick tree
- number theory
- python3
- JavaScript
- 세그먼트 트리
- nearest common ancestor
- 백준
- javascript array
- shell
- Shell Programming
- stack
- segment tree
- RUBY
- bash
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |