728x90
728x90

문제 링크 : www.acmicpc.net/problem/1463

 

1463번: 1로 만들기

첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.

www.acmicpc.net

 

 

문제

정수 X에 사용할 수 있는 연산은 다음과 같이 세 가지 이다.

  1. X가 3으로 나누어 떨어지면, 3으로 나눈다.
  2. X가 2로 나누어 떨어지면, 2로 나눈다.
  3. 1을 뺀다.

정수 N이 주어졌을 때, 위와 같은 연산 세 개를 적절히 사용해서 1을 만들려고 한다. 연산을 사용하는 횟수의 최솟값을 출력하시오.

입력

첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.

출력

첫째 줄에 연산을 하는 횟수의 최솟값을 출력한다.

 

 

 

 


 

 

 

접근 방법

 

dp[n] 는 n를 1로 만들기까지 필요한 연산 수이다. 

 

dp[n] = min(dp[n/3], dp[n/2], dp[n-1]) 로 구하는데, 이때 n이 3 또는 2로 나눠지지 않을 경우에는 최소값 후보에서 제외해야한다.

(어떤 수로 초기화해야하는가에 대한 것은 n이 최대(10^6)일 때 n에서 1을 만들기까지 필요한 연산의 최대값으로 초기화해주면 된다. 즉 10^6에서 1씩 빼는 연산으로만 1을 만들때 필요한 연산의 수인 10^6으로 초기화하면 된다. 본인은 혹시 몰라서 그것보다 더 큰수로 초기화했다.)

 

 

1부터 9까지 노가다로 구해보자.

i 1 2 3 4 5 6 7 8 9
dp[i] 0 1 1 2 3 2 3 3 2

그 다음 n=10일때를 생각해보자.

n은 3으로 나눠떨어지지 않으므로

10->5->...

10->9->...

이 두가지 경우를 생각해볼 수 있다.

dp[5] = 3이고 dp[9] = 2이므로 아래 경우로 계산했을 때 최소 연산으로 1을 만들 수 있다.

즉 dp[10] = dp[9] + 1 인 것이다. 

 

 

 

#include <iostream>
#include <algorithm>

using namespace std;

int X;
int ans = 1000000;

int dp[1000001] = {0};

//dp[a], dp[b], dp[c] 중 최소값 리턴
int searchMin(int a, int b, int c){
    
    int min1, min2;
    
    if(dp[a]<dp[b]) {
        min1 = dp[a];
        if(dp[a]<dp[c]) { min2 = dp[a]; }
        else min2 = dp[c];
        
    }
    else{
        min1 = dp[b];
        if(dp[b]<dp[c]) { min2 = dp[b];}
        else min2 = dp[c];
    }
    return min2;
}

int main(){
    
    int X; cin >> X;
    
    //n이 3 또는 2로 나눠지지 않을 때 최소값을 계산하는 searchMin()에서 영향을 주지 않기 위해 큰 수로 초기화한다.
    for(int i = 0; i<1000001; i++){dp[i] = 10000000;}
    
    int fir=0, sec=0, trd=0, mini=0; dp[0] = 0; dp[1] = 0;

    for(int i = 2; i<=X; i++){
        
        fir = i; sec= i; trd = i;
        
        if(i%3 == 0){
            fir = i/3;
        }
        if(i%2 == 0){
            sec = i/2;
        }
        
        trd = i-1;
        
        mini = searchMin(fir, sec, trd);
        dp[i] = mini + 1;
        
    }
    cout << dp[X];
    
    return 0;
}

 

 

 

728x90
728x90

문제 링크 : www.acmicpc.net/problem/1699

 

1699번: 제곱수의 합

어떤 자연수 N은 그보다 작거나 같은 제곱수들의 합으로 나타낼 수 있다. 예를 들어 11=32+12+12(3개 항)이다. 이런 표현방법은 여러 가지가 될 수 있는데, 11의 경우 11=22+22+12+12+12(5개 항)도 가능하다

www.acmicpc.net

 

문제

어떤 자연수 N은 그보다 작거나 같은 제곱수들의 합으로 나타낼 수 있다. 예를 들어 11=32+12+12(3개 항)이다. 이런 표현방법은 여러 가지가 될 수 있는데, 11의 경우 11=22+22+12+12+12(5개 항)도 가능하다. 이 경우, 수학자 숌크라테스는 “11은 3개 항의 제곱수 합으로 표현할 수 있다.”라고 말한다. 또한 11은 그보다 적은 항의 제곱수 합으로 표현할 수 없으므로, 11을 그 합으로써 표현할 수 있는 제곱수 항의 최소 개수는 3이다.

주어진 자연수 N을 이렇게 제곱수들의 합으로 표현할 때에 그 항의 최소개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 자연수 N이 주어진다. (1 ≤ N ≤ 100,000)

출력

주어진 자연수를 제곱수의 합으로 나타낼 때에 그 제곱수 항의 최소 개수를 출력한다.

 

 

 

 


 

접근 방법1

제곱수를 기준으로 생각했을 때, 항의 개수가 최소가 되려면 그 제곱수를 반드시 포함해야 한다고 생각했다.

예를 들어 18을 제곱수로 나타낸다고 하면, 

16<18<25 이기 때문에 18을 나타내려면 무조건 16이 들어가야한다고 생각했다.

그런데 예외로 18 = 9+9 로 16+1+1 보다 더 짧게 나타낼 수 있었다.

 

접근 방법 2 

18 을 자연수 2개의 합으로 나타낸다고 했을 때, 그 경우의 수를 모두 검사해서 최소가 될 때를 찾았다.

(1, 17)

(2, 16)

(3, 15)

(4, 14)

(5, 13)

(6, 12)

(7, 11)

(8, 10)

(9, 9) 

그러나 시간 초과가 떴다. 다시 생각해보니 이 중에 최소가 될 수 있는 후보는 제곱수가 포함된 경우였다.

그래서 1,4,9,16이 포함된 경우만 다시 검사해서 최소일 때를 찾아주었다.

즉,

dp[18-1] + dp[1]  => (16+1)+1

dp[18-4] + dp[4] =>  (9+4+1)+4

dp[18-9] + dp[9]  => 9 + 9

dp[18-16] + dp[16] => (1+1)+16

 

#include <iostream>
#include <algorithm>

using namespace std;

int dp[100489] = {0};

int main(){
     for(int i = 1; i<=100000; i++){
         dp[i] = 10000;
     }
    
    int N;
    cin >> N;

    dp[0] = 0; dp[2] = 2; dp[3] = 3;
    for(int i = 1; i*i<=100000; i++){
        dp[i*i] = 1;
    }
    
    for(int i = 1; i<=316; i++){
        for(int j = i*i+1; j<=(i+1)*(i+1)-1; j++){
            for(int k = 1; k<=i; k++){
                dp[j] = min(dp[j], dp[j-k*k] + dp[k*k]);
            }
        }
    }

        cout << dp[N];

    
    
    return 0;
}

 

 

 

728x90

+ Recent posts