5 분 소요

실습 4

문제 설명

자연수 n이 매개변수로 주어집니다. n을 3진법 상에서 앞뒤로 뒤집은 후, 이를 다시 10진법으로 표현한 수를 return 하도록 solution 함수를 완성해주세요.

제한사항

n은 1 이상 100,000,000 이하인 자연수입니다.

입출력 예

n result
45 7
125 229


n (10진법) n (3진법) 앞뒤 반전(3진법) 10진법으로 표현
45 1200 0021 7

내가 제출한 코드

#include <string>
#include <vector>
#include <iostream>

using namespace std;

int getThree(int n)
{
    int ans = 0;
    int k = 1;
    for (int i = n; i > 0; i /= 3, k *= 10)
    {
        int temp = i % 3;
        ans += temp * k;
    }
    return ans;
}
int getTen(int n)
{
    int ans = 0;
    int k = 1;
    for (int i = n; i > 0; i /= 10, k *= 3)
    {
        ans += (i % 10) * k;
    }
    cout << "getTen" << ans << endl;
    return ans;
}
int getReverse(int n)
{
    int ans = 0;
    string temp = to_string(n);
    string ans_str = "";
    for (int i = temp.size() - 1; i >= 0; i--)
    {
        ans_str += temp[i];
    }
    ans = stoi(ans_str);
    cout << "getReverse: " << ans << endl;
    return ans;
}
int solution(int n)
{
    int answer = 0;
    int three = getThree(n);
    int rev = getReverse(three);
    answer = getTen(rev);
    cout << answer << endl;
    return answer;
}

근데 이렇게 되면 반만 맞게 되는데 그 이유가 입력 제한 범위가 커서 그 수가 3진법으로 바꾸게 되면 2경이 된다고 한다. 그래서 결국에는 string을 사용해서 해야된다고 한다. ,,,

교수님 코드

미친 코드,,,,ㅎ

전반적으로 봤을 때, 처음 input 값은 int지만 string으로 사용하기 위해서 myItos 사용하고 그 n을 changeToK를 통해서 K진법으로 바꾸게 된다. 그 다음 converter 클래스를 사용해서 reverse를 한 다음에 뒤집힌 string n에 대해서 getDec을 통해서 10진수를 구하게 된다.

#include <iostream>
#include <string>

using namespace std;

class Number
{
private:
    string n;
    int k;
    void myItos(int n); // itos

public:
    Number(int n);
    void changeToK(int k); // k진법으로 바꾸는 함수
    int getDec();          // 문자열을 10진법 숫자(int)로 바꾸어서 리턴
    string getN();         // 멤버 변수 n(string)을 리턴
    void setN(string n);   // 멤버 변수 n(string)을 설정
};

class Converter
{
public:
    static string reverse(string str); // static으로 둠. 그 이유는 나중에 설명 예정.
};
string Converter::reverse(string str)
{
    string ret = "";
    cout << "reverse: " << str << endl;
    for (int i = str.length() - 1; i >= 0; i--)
    {
        ret += str[i];
        cout << "reverse: " << ret << endl;
    }
    return ret;
}

// constructor
Number::Number(int n)
{
    myItos(n);
    this->k = 10; // 처음 입력받은 수는 10진수
}
// 10진수 n이 들어왔을 때, int에서 string으로 바꾸어줌
void Number::myItos(int n)
{
    this->n = "";
    for (int m = n; m > 0; m /= 10)
    {
        this->n = (char)('0' + (m % 10)) + this->n;
    }
    cout << "my_itos: " << this->n << endl;
}
void Number::setN(string n)
{
    this->n = n;
}
// string n(k진수) -> int(10진수)로 바꾸어주는 함수
int Number::getDec()
{
    int i = 1;
    int num = 0;
    for (int m = n.length() - 1; m >= 0; m--)
    {
        num += (n[m] - '0') * i;
        i *= k;
    }
    return num;
}
// string n -> getDec을 통해서 int dec(n의 값을 dec에 저장)로 변환 -> int에서 k진수로 변환된 값을 string n에 저장
void Number::changeToK(int k)
{
    int dec = getDec();
    this->k = k;
    int i = 1;
    this->n = "";
    for (int m = dec; m > 0; m /= k)
    {
        this->n = (char)('0' + (m % k)) + this->n;
    }
}
// string인 n을 리턴해주는 함수
string Number::getN()
{
    return n;
}
int solution(int n)
{
    int answer = 0;

    // 최종 풀이
    Number mynum(n);
    mynum.changeToK(3);
    mynum.setN(Converter::reverse(mynum.getN()));
    cout << mynum.getN() << endl;
    answer = mynum.getDec();
    cout << mynum.getN() << endl;
    cout << mynum.getDec() << endl;
    return answer;
}

main

int solution(int n)
{
    int answer = 0;

    // 최종 풀이
    Number mynum(n);
    mynum.changeToK(3);
    mynum.setN(Converter::reverse(mynum.getN()));
    cout << mynum.getN() << endl;
    answer = mynum.getDec();
    cout << mynum.getN() << endl;
    cout << mynum.getDec() << endl;
    return answer;
}

inheritance

상속이 왜 중요하냐?

확장을 가능하게 하여 효율성을 증가시킨다. 기존 클래스를 이용해서 확장하는 방식으로 상속을 사용한다.
내가 다른 클래스에 대한 source code가 없어도 된다는게 큰 장점이다.

OOP의 신념(?) 자체가 생산성을 높이기 위해서 나오게 되었고, 생산성을 높이기 위해서는 코드의 재사용이 필요했다. 근데 code reuse는 단순이 코드 재반복만을 의미하지 않는다. 누군가 만들어놓은 클래스가 100% 내가 원하는 형태일 수도 있지만 그렇지 않을 수 있기 때문이다.
C 시절에는 lib이라는 개념이 있긴 있었다. scanf와 같은 함수가 그 예인데, 이 경우는 누군가 만들어 놓은 함수를 100% 그대로 사용하는 형태이다. 조금 변형해서 쓰는 reuse 형식이 더 많이 사용되는데 C++ 당시에는 코드를 open 하고 싶지 않았다. 이걸 가능하도록 한게 class 상속이다.

내가 기능에 대해서 바꾸고 싶다면 오버라이딩을, 멤버 변수나 새로운 기능을 추가하고 싶다면 새로운 코드를 자식 클래스에 작성하면 된다.

부모 자식 관계에도 private가 필요. 따라서 자식 클래스는 부모 클래스의 private 멤버를 사용할 수 없다(근데 ㅋㅋ friend는 가능,,) 여튼 자식 클래스에서 public에만 접근이 가능하면 사용에 제약이 있으므로 protected를 만들어서 자식 클래스가 access 가능하도록 하였다.

  friend class child class
private O X
protected O O
public O O

사용 예시

#include <iostream>
using namespace std;

class CPolygon
{
protected:
    int width, height;

public:
    void set_values(int a, int b)
    {
        width = a;
        height = b;
    }
};
class CRectangle : public CPolygon
{
public:
    int area()
    {
        return (width * height);
    }
};
class CTriangle : public CPolygon
{
public:
    int area()
    {
        return (width * height / 2);
    }
};

int main()
{
    CRectangle rect;
    CTriangle trgl;
    rect.set_values(4, 5);
    trgl.set_values(4, 5);
    cout << rect.area() << endl;
    cout << trgl.area() << endl;
    return 0;
}

CPolygon > CRectangle, CTriangle

클래스를 정의해줄 때 :public CPolygon 을 통해서 CPolygon을 상속받고 있는 자식 클래스임을 알 수 있다. 여기서 public으로 상속을 받았으므로 부모 클래스의 private, public 제한 정도를 그대로 상속 받도록 하겠다는 의미이다.
width와 height는 protected 멤버 변수이므로 자식 클래스가 접근이 가능하다.

이 예제를 통해서 OOP의 장점을 확인할 수 있는데 자식 클래스에 각각 area() 함수가 새로 정의된 것을 확인할 수 있다. 삼각형과 직사각형의 넓이를 구하는 공식이 다르므로 부모가 정의해놓은 정의를 그대로 reuse(또는 overriding)를 하고 서로 달라지는 함수에 대해서만 자식 클래스에서 추가하였다.

댓글남기기