문제

8진수 2진수 백준 1212번


리뷰

8진수 한 자리는 2진수 세 자리로 표현할 수 있다.

예를 들어, 8진수 7은 2진수로 111 이다. 111은 곧 4+2+1 == 7 이기 때문이다.

8진수 1 자리가 들어왔을 때, 2진수 3자리로 바꿔서 스택에 넣어줬다.

가장 작은 자리수 부터 계산하니까. 스택에 넣은다음에 출력할 때는 올바른 순서로 나오기 때문에 스택을 선택했다.

8진수를 2로 나눈 나머지를 스택에 저장한다. 이 때, 나눗셈을 3번 한다.


처음에는 틀렸는데, 질문게시판을 보니까 0을 넣으면 0이 나와야 한다는 댓글을 봤다.

내코드에서는 0넣으면 아무것도 출력이 안됬었다.

이유는 스택에 001100 가 들어가 있다면, 1100 으로 출력하기 위해서였다. 0으로 시작하는 이진수는 없기 때문이다.

처음에는 스택의 top이 0이고, flag 가 false 라면 pop만 하고, 출력하지 않았다.

처음 1이 나올때 부터, flag를 true로 바꿔줘서, top 출력과 pop 둘 다 하도록 했다.

    bool flag= false;

    while(!answer.empty()){ // 스택 출력 

        if(answer.top() == 0 && flag == false){
            answer.pop();
            continue;
        }else{
            flag = true;
        }

        cout << answer.top();
        answer.pop();
    }

입력이 8진수 0이라면, 이진수도 답이 0이 나와야 하니까

while문의 첫번째 if문 조건을 수정했다.

    bool flag= false;

    while(!answer.empty()){ // 스택 출력 

        if(answer.top() == 0 && flag == false && answer.size() != 1){
            answer.pop();
            continue;
        }else{
            flag = true;
        }

        cout << answer.top();
        answer.pop();
    }

맞은 코드 1 (내가 짠 코드)

#include <iostream> 
#include <string>
#include <cstring> // strcpy()
#include <stack>
using namespace std;

string st;
char N[333335];
stack<int> answer;

int main(void){

     cin >> st;

     strcpy(N, st.c_str()); // string을 char 배열로 복사 

     int len = st.length(); // 8진수의 길이 

     for(int i = len-1; i >= 0 ; i--){

        int temp = N[i]-48;

        if(temp == 0){
            answer.push(0); answer.push(0); answer.push(0);
            continue;
        }

        for(int j = 0; j < 3; j++){ // 0이 아니라면,  2로 나눈 나머지를 stack에 push  
             answer.push(temp % 2);
             temp /= 2; 
        }
    }

    bool flag= false;

    while(!answer.empty()){ // 스택 출력 

        if(answer.top() == 0 && flag == false && answer.size() != 1){
            answer.pop();
            continue;
        }else{
            flag = true;
        }

        cout << answer.top();
        answer.pop();
    }

    return 0;
} 



내 코드가 어수선 하다는 느낌이 들었다. 다른 분들 코드를 찾아 보고 배웠다.

8진수를 2진수로 나타낼 수 있는 8가지 문자열을 미리 배열에 저장해두고 활용하는 방식이다.

first는 첫 숫자가 0이 나오면 안되니까 만들어 둔 배열이다. 0으로 시작하는 이진수는 없기 때문이다.

string first[8] = {"0", "1", "10", "11", "100", "101", "110", "111"};
string other[8] = {"000", "001", "010", "011", "100", "101", "110", "111"};

string st;
cin >> st;

cout << first[st[0] - '0'] ; // 문자로 된 숫자 하나에서 '0' 을 빼주면 숫자가 나온다  

first [ st[0] - '0' ] 이게 뭐지 싶었는데.

예를 들어, st[0] 이 문자 '3' 이라면, 문자 3에서 문자 0을 뺴야 숫자 3을 얻을 수 있다.

// st [0] == '3'

 first  [ '3' - '0' ] = first [3] 
 과 같다. 

 other[st[i] - '0']; 도 같은 구조다. 
 // st[0] 이 문자 5라면  (8진수로 5를 받았다면,) 
 other[ '5'-'0' ] == other [5] 가 된다. 이렇게 8진수를 2진수로 변환한다.  

가독성도 좋고 아주 배우고 싶은 코드여서 정리한다.


맞은 코드 2

#include <iostream> 
#include <string>
using namespace std;

string first[8] = {"0", "1", "10", "11", "100", "101", "110", "111"};
string other[8] = {"000", "001", "010", "011", "100", "101", "110", "111"};

int main(void){

    string st;
    cin >> st;

    cout << first[st[0] - '0'] ; // 문자로 된 숫자 하나에서 '0' 을 빼주면 숫자가 나온다  

    for(int i = 1; i < st.size(); i++){
        cout << other[st[i] - '0'];
    }

    return 0;
} 
728x90

'알고리즘 > 백준' 카테고리의 다른 글

숨바꼭질6 백준 17087번 c++  (0) 2020.09.19
링크와 스타트 백준 15661번 c++  (0) 2020.09.17
2진수 8진수 백준 1373번  (0) 2020.09.15
진법변환 백준 2745번  (0) 2020.09.15
진법변환2 백준 11005번 c++  (0) 2020.09.15

문제

2진수 8진수 백준 1373번


리뷰

2진수의 3 자리를 8진수의 1 자리로 만들 수 있다.

예를 들어 2진수 11001100 이 있다.

맨 오른쪽 부터 2의 0승, 2의 1승, 2의 2승이 곱해진 수다. ( 4, 2, 1)

3의 배수의 길이일때 이것이 성립한다.

11001100은 길이가 8이다. 8진수로 계산할 때는 11 001 100 으로 나눠질 수 있다.

첫번째 1과 두번째 1은 따로 처리해줘야 한다.

11 001 100 

11 을 8진수로 바꾼다. 
(2^1)x 1  + (2^0)x 1   // 2자리 일때는 2의 1승부터 시작!
    = 2 + 1 
    = 3 

001 을 8진수로 바꾼다. 
(2^2)x 0  + (2^1)x 0  +  (2^0)x 1   // 3자리 일때는 2의 2승부터 시작!
    = 0 + 0 + 1
    = 1    

100 을 8진수로 바꾼다. 
(2^2)x 1  + (2^1)x 0  +  (2^0)x 0 
    = 4 + 0 + 0
    = 4

이진수 문자열 길이가 4, 5, 6인 세 가지 경우가 있다.

이진수 문자열 길이가 4인 경우, (길이 % 3 == 1)

맨 앞의 수를 (2^0)x ? 으로 계산해주고,

나머지 3개의 수를 (2^2)x ? + (2^1)x ? + (2^0)x ? 식으로 계산하면 된다.


맞은 코드

#include <iostream> 
#include <cstring> //strlen() 
using namespace std;

char ch[1000001];

int main(void){

    scanf("%s", &ch); 

    int len = strlen(ch);

    if(len % 3 == 1){
        printf("%d", ch[0]-48 );

    }else if(len % 3 == 2){
        printf("%d",  2*( ch[0]-48 ) + ch[1]-48 );
    }    

    // 나머지 3의 배수로 떨어지는 길이만큼. len%3으로 시작해야 한다.
    for(int i = len%3; i < len; i += 3 ){
        printf("%d", 4 * ( ch[i]-48 ) +   2 * ( ch[i+1]-48 ) +  ch[i+2]-48 );
    }

    return 0;    
} 

728x90

'알고리즘 > 백준' 카테고리의 다른 글

링크와 스타트 백준 15661번 c++  (0) 2020.09.17
8진수 2진수 백준 1212번  (0) 2020.09.16
진법변환 백준 2745번  (0) 2020.09.15
진법변환2 백준 11005번 c++  (0) 2020.09.15
GCD합 백준 9613번  (0) 2020.09.15

문제

진법변환 백준 2745번


리뷰

B진법 수 N을 10진법으로 바꿔서 출력하는 프로그램 작성하기.

B진법은 2진법 부터 최대 36진법까지 입력된다.

N을 10진법으로 바꾸면 항상 10억보다 작거나 같다.


문자열로 N을 입력받고, 마지막 자리 문자 부터 B의 0승, B의 1승, B의 2승... 이렇게 곱해서 누적한 결과를 출력했다.

string 을 char 배열로 바꾸는 것은 cstring 의 strcpy 를 이용했다.

#include <cstring>
#include <string>

string N;
char ch[30];
strcpy(ch, N.c_str()); // string 을 char N에 복사  

*문자 '9'는 57에 대응하고 문자 'A'는 65 임을 이용했다. *

    if('9' < ch[i]){ // 알파벳으로 표현한 경우, A는 65임을 이용 
        answer = answer + ( pow(B, n) * (ch[i]-55) );
    }else{ // 문자 '9'는 57임을 이용 
            answer = answer + ( pow(B, n) * (ch[i]-48) );
    }

char 배열 길이 때문에 런타임 에러 발생

string 을 char 배열로 변환해서 쓰는 부분에서 런타임 에러를 이 글을 읽고 도움받았다.

예를 들어, 2진법이면 0과 1밖에 표현 안하니까 다른 진법에 비해 문자열이 길게 들어올 것이다.

N은 항상 10억보다 작거나 같다. 10억 ( 1,000,000,000)을 2진수으로 표현하면

0011 1011 1001 1010 1100 1010 0000 0000

따라서 char 배열은 30자리 이상으로 만드는 것이 안전하다.


맞은 코드

#include <iostream> 
#include <string>
#include <cstring> // strcpy()
#include <cmath> // pow()
using namespace std;

string N;
char ch[30];
int B, answer;

int main(void){

    cin >> N >> B;

    strcpy(ch, N.c_str()); // string 을 char N에 복사  
    int n = 0; // B의 자승 표현을 위한 변수 

    for(int i = N.length()-1; i >= 0; i--){

        if('9' < ch[i]){ // 알파벳으로 표현한 경우, A는 65임을 이용 
            answer = answer + ( pow(B, n) * (ch[i]-55) );
        }else{ // 문자 '9'는 57임을 이용 
            answer = answer + ( pow(B, n) * (ch[i]-48) );
        }
        n++;
    }

    cout << answer;

    return 0;    
} 

백준 질문게시판에 글 올려주시고 댓글 달아주시는 분들 모두 다 감사하다.

728x90

'알고리즘 > 백준' 카테고리의 다른 글

8진수 2진수 백준 1212번  (0) 2020.09.16
2진수 8진수 백준 1373번  (0) 2020.09.15
진법변환2 백준 11005번 c++  (0) 2020.09.15
GCD합 백준 9613번  (0) 2020.09.15
조합 0의 개수 백준 2004번  (0) 2020.09.15

문제

진법변환2 백준 11005번

리뷰

10진수 N을 B진수로 바꾸려면, N을 B로 나눈 나머지를 적어주다가 거꾸로 출력하면 된다.

계속 B로 나누는 것이다.

    while( N > 0){
        answer.push_back( N % B );
        N /= B; 
    }

문제의 조건에서 10 부터는 알파벳으로 표현하라고 했다.

그래서 10부터는 아스키코드를 이용해서 출력했다. 10일 때는 'A'를, 11 일 때는 'B' 로 출력해야 한다.

'A'는 65니까, 10 + 55 로 'A' 를 표현할 수 있다.

    for(int i = answer.size()-1; i >= 0 ; i--){ // 거꾸로 출력  

        if(answer[i] < 10){
            cout<< answer[i];
        }else{
            printf("%c", answer[i]+55);  // char 로 'A'는 10진수 65와 같다 
        }
    }

맞은 코드 1

#include <iostream> 
#include <vector>
using namespace std;

int main(void){

      int N = 0, B = 0; 
    vector<int> answer; 

    cin >> N >> B;

    while( N > 0){
        answer.push_back( N % B );
        N /= B; 
    }

    for(int i = answer.size()-1; i >= 0 ; i--){ // 거꾸로 출력  

        if(answer[i] < 10){
            cout<< answer[i];
        }else{
            printf("%c", answer[i]+55);
        }
    }

    return 0;    
} 

stack을 이용한 다른 풀이

어차피 거꾸로 출력해야 하니까 '나머지 계산'한 것을 전부 스택에 push 하고,

스택일 빌 때까지 전부 pop 하면 된다.

알파벳을 출력하는 경우를 고려해서 char 를 저장하는 스택을 이용했다.

그런데 0부터 9까지는 어떻게 스택에 넣지? 싶어서 구글링 해보니까 4, 5 같은 숫자를 넣을때 앞에 문자로 '0'을 넣어준다.

맞은 코드 2

#include <iostream> 
#include <stack>
using namespace std;

int main(void){

      int N = 0, B = 0; 

    cin >> N >> B;

    //printf("%d %d\n", '9', 'A'); // 57 65  7차이  
    stack<char> answer;

    while(N){

        int temp = N % B;

        if(temp >= 10){
            temp += 7;
        }

        answer.push('0'+temp);

        N /= B;
    }    

    while(!answer.empty()){
        cout << answer.top();
        answer.pop();
    }

    return 0;    
} 
728x90

'알고리즘 > 백준' 카테고리의 다른 글

2진수 8진수 백준 1373번  (0) 2020.09.15
진법변환 백준 2745번  (0) 2020.09.15
GCD합 백준 9613번  (0) 2020.09.15
조합 0의 개수 백준 2004번  (0) 2020.09.15
소인수분해 백준 11653번  (0) 2020.09.14

문제

GCD합 백준 9613번


리뷰

최대공약수 GCD를 구할 줄 알면 풀 수 있는 문제였다.

이중 for문으로 2개의 수를 고른다.

두 수의 최소공배수를 리턴해서 sum에 누적한다.


유클리드 호제법으로 최대공약수를 구하기

int GCD(int a, int b){
    if(b == 0) return a;
    else return GCD(b, a%b);
}

유의할 점

누적하는 sum 의 자료형는 int가 아니라 long long 으로 써야한다.

수의 개수 n (1 < n ≤ 100)

입력으로 주어지는 수는 1,000,000을 넘지 않는다.

백만과 그 비슷한 크기 수 n개의 모든 쌍의 최대공약수의 합을 누적하려면 int 로는 안된다.

int의 최대 값은 대략 2,147,000,000 이다. (2,147,483,647)

n개가 99이고, 그 주어진 수는 모두 999999 라면. 가능한 쌍은 99 x 98 해서 9702가지 쌍을 만들 수 있다.

99 x 98 x 990,000 = 9,701,990,298

int 의 범위를 초과하기 때문에 sum 변수는 long long 으로 해야 한다.


맞은 코드

#include <iostream> 
using namespace std;

int TC;
int num[101];


int GCD(int a, int b){
    if(b==0) return a;
    else return GCD(b, a%b);
}

int main(void){

    cin >> TC;     

       while(TC--){ // 테스트 케이스 만큼 반복 

           long long sum = 0; // 모든 쌍의 GCD 값을 누적할 변수 
        int total = 0;

           cin >> total; 

           for(int i = 0; i < total; i++){ // total 개의 수를 배열에 집어넣는다 
               cin >> num[i];
        }

        for(int i = 0; i < total; i++){ // 이중 for문으로 2개의 수를 골라 GCD를 구한다 

            for(int j = i+1; j < total; j++){
                sum += GCD(num[i], num[j]);
            }    
        }

        printf("%lld\n", sum);
    }

    return 0;    
} 
728x90

'알고리즘 > 백준' 카테고리의 다른 글

진법변환 백준 2745번  (0) 2020.09.15
진법변환2 백준 11005번 c++  (0) 2020.09.15
조합 0의 개수 백준 2004번  (0) 2020.09.15
소인수분해 백준 11653번  (0) 2020.09.14
골드바흐의 추측 백준 6588번  (0) 2020.09.14

문제

조합 0의 개수 백준 2004번

리뷰

문제를 풀기 전에, 팩토리얼 0의 개수 문제를 다시 풀고 왔다.

아래는 조합을 구하는 식이다. 조합은 팩토리얼의 나눗셈과 곱셈으로 이루어져 있다.

그래서 이 문제는 n! 과 r! 과 (n-r)! 을 각각 계산해야 한다.

nCm의 끝자리 0의 개수는 10이 얼마나 곱해졌는지가 좌우한다.

10은 2x5 다.

그래서 n! 과 r! 과 (n-r)! 이렇게 3개의 팩토리얼에 2와 5가 몇 번 곱해져있는지 계산해야 한다.

five_cnt 함수는 5의 개수를 리턴한다. two_cnt 함수도 똑같은 구조다.

int five_cnt(int n){ // 5의 개수 구한다 
    int cnt = 0;

    for(long long i = 5; 1 <= (n/i); i *= 5){
        cnt += (n/i);
    }

    return cnt;
}

n과 m은 최대 2,000,000,000 의 숫자가 주어진다.

5를 곱해가면서 n을 나눌꺼니까 int의 표현범위를 넘을 것을 감안하여 long long 으로 선언한다.

two_cnt 함수는 2의 개수를 리턴한다.

two_cnt와 five_cnt 함수로 2와 5의 개수를 구하되, 둘 중에 작게 나온 값이 인수 10의 개수가 될 것이다.

n C r 조합에서 유의할 점 2가지

r == 0이면, 항상 1이다. 아무것도 안고르기 때문이다. n개중에 0개.

n == r 이면, 항상 1이다. 전부 고르기 때문이다. n개중에 n개.

따라서 n C m 이 주어졌을 때, m이 1인 경우, 2와 5의 숫자를 안구해도 된다. 어차피 1이기 때문이다.

n==m 인 경우에도 1이니까 2와 5의 숫자를 안구해도 된다.

    // 5, 2의 개수를 각각 구해서 작은 쪽을 출력한다
    five += five_cnt(n);
    if(m > 0) five -= five_cnt(m);
    if(n != m) five -= five_cnt(n-m);

맞은 코드

#include <iostream> 
#include <algorithm>

using namespace std;

int n, m;
int two, five;

int two_cnt(int n){ // 2의 개수 구한다 
    int cnt = 0;

    for(long long i = 2; 1 <= (n/i); i *= 2){
        cnt += (n/i);
    }

    return cnt;
}

int five_cnt(int n){ // 5의 개수 구한다 
    int cnt = 0;

    for(long long i = 5; 1 <= (n/i); i *= 5){
        cnt += (n/i);
    }

    return cnt;
}

int main(void){

    cin >> n >> m;     

    // 5, 2의 개수를 각각 구해서 작은 쪽을 출력한다
    five += five_cnt(n);
    if(m > 0) five -= five_cnt(m);
    if(n != m) five -= five_cnt(n-m);

    two += two_cnt(n);
    if(m > 0) two -= two_cnt(m);
    if(n != m) two -= two_cnt(n-m);

    cout << min(two, five);

    return 0;    
} 
728x90

문제

소인수분해 백준 11653번


리뷰

가장 작은 소수 2부터 루트 N까지 나눠보면서 소인수분해 한다.

루트 N까지 나누는 이유는 에라토스테네스의 체를 풀어보면 알 수 있다.

미리 N까지의 모든 소수를 구해놓지 않고서도 반복문만으로 풀 수 있는 문제다.


for(int i = 2; i*i <= N; i++){  // 나누어 떨어지지 않는다면 i 증가 

    while( N % i == 0){ // 나누어 떨어진다면 출력 
        printf("%d\n", i);
        N /= i;
    }
}

예를 들어, N=12 인 경우

12는 2로 나누어 떨어지므로. 2 출력. N == 6

6은 2로 나누어 떨어지므로 2 출력. N == 3

이 때, 루트 N은 3과 4 사이의 값이다. 이미 N을 초과해버린다. 그래서 N이 3인채로 반복문이 종료된다.

따라서 for문이 끝나고 나서 N이 1 초과인 경우에 N을 출력 해줘야 한다.

    for(int i = 2; i*i <= N; i++){  // 나누어 떨어지지 않는다면 i 증가 

        while( N % i == 0){ // 나누어 떨어진다면 출력 
            printf("%d\n", i);
            N /= i;
        }
    }

    if(N > 1) cout << N;  // 위의 반복문이 i*i <= N 까지 동작하기 때문.

맞은 코드 1

#include <iostream> 
using namespace std;

int main(void){

     int N = 0;
    int i = 2; 

    cin >> N;

    if(N == 1) return 0; // 1은 소수가 아니다 

    for(int i = 2; i*i <= N; i++){  // 나누어 떨어지지 않는다면 i 증가 

        while( N % i == 0){ // 나누어 떨어진다면 출력 
            printf("%d\n", i);
            N /= i;
        }
    }

    if(N > 1) cout << N;  // 위의 반복문이 i*i <= N 까지 동작하기 때문.

    return 0;    
} 

맞은 코드 2

N이 더 이상 나눠지지 않을 때 까지 i로 나눈다

#include <iostream> 
using namespace std;

// 소인수분해 11653 

int main(void){

     int N = 0;

    cin >> N;

    if(N == 1) return 0;

    int i = 2; 

    while( N>1 ){

        if( N % i == 0){
            printf("%d\n", i);
            N /= i;
        }else{
            i++;
        }
    }
    return 0;    
} 

728x90

+ Recent posts