※ 이번 문제에서 알게된 점

§  재귀함수로 구현하는 gcd, lcm 함수

// 최대공약수
auto gcd(long long A, long long B){
    if (B == 0)
        return A;
    else
        return gcd(B, A%B);
}

 

// 최소공배수
auto lcm (long long A, long long B){
    return A*B/gcd(A,B);
}

 

 

 

 

§ my solution

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

// 최대공약수
auto gcd(long long A, long long B){
    if (B == 0)
        return A;
    else
        return gcd(B, A%B);
}

// 최소공배수
auto lcm (long long A, long long B){
    return A*B/gcd(A,B);
}

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);

    long long A, B;   cin >> A >> B;
    cout << lcm(A,B);
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

백만자리라는 문제를 안읽고 풀어서 틀렸다. lon long을 써줘야 함.

 

 

 

 

 

 

 

 

 

§ my solution

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

auto gcd(long long a, long long b){
    if (b == 0)
        return a;
    else
        return gcd(b, a%b);
}

auto lcm(long long a, long long b){
    return a*b / gcd(a, b);
}

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);

    int num;    cin >> num;
    vector<long long> v1;
    vector<long long> v2;

    for (int i = 0; i < num; i++) {
        int a, b;   cin >> a >> b;
        v1.push_back((a));
        v2.push_back((b));
    }

    for (int i = 0; i < num; i++) {
        cout << lcm(v1.at(i), v2.at(i)) << '\n';
    }
}

 

 

※ 이번 문제에서 알게된 점

§  sort(v.begin(), v.end(), custom_func) 함수

 

1. 먼저 vector에 include 되어 있는 pair를 선언해 입력을 받으면 다음과 같다.

vector< pair<int, int> > p;

for (int i = 0; i < num; i++) {
    int n1, n2; cin >> n1 >> n2;
    p.push_back(make_pair(n1, n2));
}

 

 

2. 그 후 sort를 해줘야 하는데, 이때 문제는 sort를 해주면 p.first를 기준으로 정렬 된다는 것.

그래서 사용자가 만든 함수로 sort(v.begin(), v.end(), custom_func) 의 custom_func에 넣어서

p.second 기준으로 정렬되도록 해야한다. 이때, custom_func의 결과값은 0, 1이 나와야 하며 보통 bool type이다.

bool compare(pair<int, int> a, pair<int, int> b) {
    if (a.second == b.second)
        return a.first < b.first;  // a.first가 작으면 true. 즉 작은게 앞에 배치가 된다.
    return a.second < b.second;       // a.second가 더 작으면 true. 즉 작은게 앞에 배치가 된다.
}
sort(p.begin(), p.end(), compare);

 

\

 

 

 

 

 

 

 

§ my solution 

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

bool compare(pair<int, int> a, pair<int, int> b) {
    if (a.second == b.second)
        return a.first < b.first;  // a.first가 작으면 true. 즉 작은게 앞에 배치가 된다.
    return a.second < b.second;       // a.second가 더 작으면 true. 즉 작은게 앞에 배치가 된다.
}

int main() {
    ios_base::sync_with_stdio(0);
    int num; cin >> num;

    vector< pair<int, int> > p;

    for (int i = 0; i < num; i++) {
        int n1, n2; cin >> n1 >> n2;
        p.push_back(make_pair(n1, n2));
    }
    sort(p.begin(), p.end(), compare);

    for (auto x:p) {
        cout << x.first << " " << x.second << '\n';
    }
}

 

 

 

 

 

 

§ my solution 

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

int main() {
    ios_base::sync_with_stdio(0);
    int N, A;   cin >> N >> A;
    vector<int> v;
    for (int i = 0; i < N; i++) {
        int num;    cin >> num;
        v.push_back(num);
    }
    sort(v.begin(), v.end());
    cout << v[A-1];
}

 

 

 

 

 

 

 

 

 

§ my solution 

- endl 대신 '\n'을 애용하자!

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

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int N;  cin >> N;
    vector<int> v;
    for (int i = 0; i < N; i++) {
        int num;    cin >> num;
        v.push_back(num);
    }
    sort(v.begin(), v.end());
    for(auto x:v)
        cout << x << '\n';
}

 

 

 

 

 

§ my solution 

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

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);

    int N; cin >> N;
    vector<int> v;
    for (int i = 0; i < N; i++) {
        int num;    cin >> num;
        v.push_back(num);
    }
    sort(v.begin(), v.end(), greater<int>());
    for(auto x:v)
        cout << x << '\n';
}

 

 

 

 

 

※ 이번 문제에서 알게된 점

§  unique() 함수

https://chan4im.tistory.com/51

 

this.algorithm(2). 중복된 원소제거, unique()

 

chan4im.tistory.com

 

 

 

 

 

 

 

§ my solution 

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

int main() {
    ios_base::sync_with_stdio(0);

    int N;  cin >> N;
    vector<int> v;
    for(int i = 0; i < N; i++){
        int num;    cin >> num;
        v.push_back(num);
    }

    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());

    for(auto x:v)
        cout << x << " ";
}

 

이번 문제는 문제 난이도 자체는 평이, 아니 쉬운 수준이었다.

그냥 배열을 합치고 algorithm 헤더의 sort만 해주면 끝나는 문제였다.

문제는 다름이 아니라 시간초과 발생 때문에 대략 2시간을 잡아먹은 것이다.

코드의 길이를 정말 최대로 줄일 수 있을만큼 줄이고 vector도 사용해보고 배열도 사용해 보았다.

하지만 그렇게 짜본 코드가 모두 시간초과가 떠버렸다.

 

결과적으로 위에 내가 짰던 코드가 틀린 것은 아니지만,

키워드 3줄만 적어주었더니 시간초과가 뜨지 않았다.

 

 

 

 

※ 이번 문제에서 알게된 점

§  ios_base::sync_with_stdio() 함수

먼저 C++의 <iostream>은 C의 <stdio.h>와 다르다. 

C++의 입출력 작업 시, C의 입출력과 동기화 되도록 설계되어 상당히 느린 입출력과정을 겪는다.

추가적으로 endl도 출력문자 출력+출력buffer를 함께 비워 시간이 많이 걸려 \n사용을 권장한다.

이때 말하는 "동기화"를 뜻하는 코드가 ios_base::sync_with_stdio이고, 이 동기화를 풀어주는 코드이다.

따라서 동기화가 풀어진다면?  => 당연히 속도를 올릴 수 있다!

ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);

 

큰 힘에는 큰 책임이 따른다고, 이런 ios_base::sync_with_stdio에도 치명적인 단점이 있다.

 

1. 그건 바로 싱글 쓰레드 (single thread) 환경은 괜찮지만, 멀티 쓰레드 (Multi Thread)환경에서는 출력순서가 어그러진다.

즉, 멀티쓰레드 환경에서의 출력순서가 보장될 수 없다는 것이다!

cf-1) 코딩테스트에서는 싱글쓰레드만 사용하기에 사용해도 무방하다.

cf-2) 다만 실무에서는 ios_base::sync_with_stdio를 쓰기보단, 속도를 올리기 위해 C입출력 방식을 권장한다.

 

 

2. 버퍼가 분리되었기에 cin과 scanf, gets, getchar 등의 C문법 (당연히 출력코드도 동일)을 같이 사용할 수 없다!

 

cf-2) 싱글, 멀티쓰레드란? https://chan4im.tistory.com/35

 

this->OS.code(4)

exit() system call, 처리불가능한 signal, CPU err, 부모 process의 명령과 같은 상황에서 process termination이 발생하며 아직 process 상태에 존재한다. (process에 존재하지만 실행에 필요한 컴퓨터 자원들을 proces

chan4im.tistory.com

 

 

 

§  cin.tie(NULL)  /  cout.tie(NULL) 함수

[정의]: istream, ostream 즉, stream을 untie 하여 자동으로 입출력 버퍼를 비우지 않게 함

cin과 cout간의 tie를 끊어주는, 즉 다른 입출력이 온다면 기존의 입출력을 비워주도록 하는 것이다.

이 tie를 끊어줌으로써 시간을 많이 절감할 수 있다.

 

[예시]

왼쪽그림에서 ios_base::sync_with_stdio(0);을 지워도 console창은 하게 뜬다.

왼쪽을 보면 cout으로 "입력"이란 문구가 출력된 후 cin이 나오는 것을 알 수 있고, 이는 익숙한 방식이다.

하지만 우측은 입력 후 출력이 모두 동시에 되는 것을 알 수 있다.

우리가 tie를 끊어서 입력 전에 출력관련 문구가 아무리 입력보다 먼저 적혀있었더라도 출력이 되지 않는다.

만약 입력전에 출력되게 하고 싶다면 cout buffer를 수동적으로 비워줘야 한다.

 

https://hegosumluxmundij.tistory.com/54

 

 

 

 

 

 

 

§ reserve() 와 resize() 의 차이

가장 고생했던 부분이 있는데 다음과 같다.

https://hoondev.tistory.com/7

§ reserve() 와 resize() 의 차이

capacity: 미리 할당해놓은 배열공간

size: 배열에 들어있는 원소의 수

 

대부분의 컨테이너 STL 자료구조들은 일정 크기를 할당(capacity)한 후 

size가 capacity를 넘으면 2배정도의 크기를 늘려 이동하는 방식으로 구현되어 있다.

 

§ reserve(N)

- vector의 capacity를 N으로 설정하는 것.

- N이 현재 capacity보다 클 때만 작동, 작으면 아무 일도 발생X

- N이 현재 capacity보다 크다면 모든 원소 reference와 iterator가 전부 무효화(invalidate)된다.

 

§ resize(N)   /   resize(N, value)

- size > N인 경우, 원소의 개수를 줄인다.

- size < N인 경우, 입력된 value가 없으면 default값인 0으로 할당한다. 있다면 value로 할당.

  그 후, size가 N이 되도록 늘린다.

 

 

 

 

 

 

§ my solution 

총 3가지 방식으로 해결하였다.

1. 배열과 배열의 포인터(주소값)로 sort 후 해결 (메모리 9708KB  /  시간 636ms)

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

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int N, M;   cin >> N >> M;
    int arr[N+M];

    for (int i = 0; i < N+M; i++) {
        cin >> arr[i];
    }
    sort(arr, arr+N+M);

    for (const auto& x:arr) {
        cout << x << " ";
    }
}

 

2. 배열을 벡터로만 바꿈 (메모리 9836KB  /  시간 688ms)

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

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int N, M;   cin >> N >> M;
    vector<int> v(N+M);
    for (int i = 0; i < N+M; i++)
        cin >> v[i];

    sort(v.begin(), v.end());

    for (int i = 0; i < N+M; i++) {
        cout << v[i] << " ";
    }
}

 

3. 정말 벡터 2개를 생성, 합친 후 정렬! (메모리 17656KB  /  시간 644ms)

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

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int N, M;   cin >> N >> M;
    vector<int> v1(N);
    vector<int> v2(M);

    for (int i = 0; i < v1.size(); i++){
        cin >> v1[i];
    }
    for (int i = 0; i < v2.size(); i++){
        cin >> v2[i];
    }

    vector<int> v3;

    v3.reserve(v1.size() + v2.size());
    v3.insert(v3.end(), v1.begin(), v1.end());
    v3.insert(v3.end(), v2.begin(), v2.end());

    sort(v3.begin(), v3.end());

    for (const auto& x:v3) {
        cout << x << " ";
    }
}

 

4. 2개의 vector를 merge와 sort를 이용해 정렬(메모리 24684KB  /  시간 544ms)

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

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0); 
    cout.tie(0);

    vector<int> v1;
    vector<int> v2;

    int N, M;   cin >> N >> M;

    for (int i = 0; i < N; i++) {
        int num;    cin >> num;
        v1.push_back(num);
    }
    for (int i = 0; i < M; i++) {
        int num;    cin >> num;
        v2.push_back(num);
    }
    vector<int> v3;

    merge(v1.begin(), v1.end(), v2.begin(), v2.end(), back_inserter(v3));

    sort(v3.begin(), v3.end());

    for (const auto& x:v3) {
        cout << x << " ";
    }
}

 

 

 

§ my solution 

추출,삽입이 빠른 덱으로 size가 1이 되기 전까지 진행

 

제일 처음 삭제

dq.erase(dq.begin());

 

처음값 back으로 push

dq.push_back(dq.front());

 

제일 front pop

dq.pop_front();

 

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

deque<int> dq;

int main() {
    int n;  cin >> n;
    for (int i = 0; i < n; i++) {
        dq.push_back(i+1);
    }
    while (dq.size() > 1) {
        cout << dq.front() << " ";
        dq.erase(dq.begin());
        dq.push_back(dq.front());
        dq.pop_front();
    }
    cout << dq[0];
}

 

 

§ my solution 

추출,삽입이 빠른 덱으로 size가 1이 되기 전까지 진행

 

제일 처음 삭제

dq.erase(dq.begin());

 

처음값 back으로 push

dq.push_back(dq[0]);

 

제일 front pop

dq.pop_front();

 

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

deque<int> dq;

int main(){
    int n;  cin >> n;
    for (int i = 0; i < n; i++) {
        dq.push_back(i+1);
    }
    while (dq.size() > 1){
        dq.erase(dq.begin());
        dq.push_back(dq[0]);
        dq.pop_front();
    }
    cout << dq[0];
}

 

 

※ 이번 문제에서 알게된 점

§ insert() 함수

dq.insert함수는 insert(pos, val);로 값이 들어가야 해서 dq.insert(dq.end()+i, dq.begin()+i);처럼 사용하지 말아야 한다.

위와 같은 의미로 다시 코드를 짜고 싶다면 다음과 같이 dq.insert(dq.end()+i, dq[i]); 와 같이 사용해야 한다.

dq.insert(dq.end()+i+1, dq[i]); // 정상적인 작동
dq.insert(dq.end()+i+1, dq.begin(i));  // dq.begin(i) 부분에 오류발생
dq.insert(dq.end()+i+1, dq.begin()+i); // dq.insert() 부분에 오류발생

 

 

 

§ front 와 begin 의 차이

가장 고생했던 부분이 있는데 다음과 같다.

 

<정답코드>

for (int i = 0; i < k-1; i++) {
    dq.push_back(dq.front());
    dq.pop_front();
}
cout << dq.front() << ", ";
dq.erase(dq.begin());

// 7 3
// <3, 6, 2, 7, 5, 1, 4>

 

<오답코드>

for (int i = 0; i < k; i++) {
    dq.insert(dq.end()+i+1, dq[i]);
    dq.erase(dq.begin()+i);
}
dq.pop_back();

// 7 3
// <2, 4, 7, 7, 7, 7, 7>

 

front()는 첫 "요소"를 반환하는 것이다.

begin()은 그 요소의 참조 즉, "주소값"을 반환한다.

즉, begin은 컬렉션을 통해 반복하는 데 사용할 수 있는 해당 요소의 iterators를 반환하지만

      front는 컬렉션의 첫 번째 요소에 대한 참조만 반환 (포인터 개념)합니다.

 

따라서 push_back(값); 메소드를 활용하기 위해서는 front를 사용하는 것이 좋다.

dq.push_back(dq.front());

 

 

 

 

 

 

§ my solution 

추출,삽입이 빠른 덱으로  k - 1번씩 밀고, 맨 앞에있는 값을 제거하는 알고리즘을, 모든 값이 없어질 때 까지 반복

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

deque<int> dq;
int main(){
    int n, k;   cin >> n >> k;
    cout << '<';


    for (int i = 0; i < n; i++) {
        dq.push_back(i+1);
    }
    while (dq.size() > 1) {
        for (int i = 0; i < k-1; i++) {
            dq.push_back(dq.front());
            dq.pop_front();
        }
        cout << dq.front() << ", ";
        dq.erase(dq.begin());
    }
    cout << dq[0];
    cout << '>';
}

 

 

§ 시행착오 

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

deque<int> dq;
int main(){
    int n, k;   cin >> n >> k;
    cout << '<';


    for (int i = 0; i < n; i++) {
        dq.push_back(i+1);
    }
    while (dq.size() > 1) {
            if (dq.size() > 2) {
                cout << dq[k-1] << ", ";
                for (int i = 0; i < k; i++) {
                    dq.insert(dq.end()+i+1, dq[i]);
                    dq.erase(dq.begin()+i);
                }
                dq.pop_back();
            }
            else if(dq.size() <= 2){
                cout << dq[0] << ", "<< dq[1];
                break;
            }
    }
    cout << dq[0];
    cout << '>';
}

왜 이따구로 풀었을까?

 

- 일단 먼저 다음과 같이 접근하였다.

1 2 3 4
                    7  6  5

             front                  back <-- 삽입
                    1 2 3 4 5 6 7
                    (1 2 3) 4 5 6 7 [1 2 3]
                    4 5 6 7 1 2
                    (4 5 6) 7 1 2 [4 5 6]
                    7 1 2 4 5
                    4 5 7 1
                    1 4 5
                    4 5 1
>> 그대로 출력       5 1 4

왜 안될까?  => k값이 3이라 저렇게 출력되는 것처럼 보일 뿐, 다음과 같은 테스트 케이스에서는 오답으로 나온다.

10 1
<1, 2, 3, 4, 5, 6, 7, 8, 9, 10>

5 5
<5, 1, 3, 4, 2>

10 4
<4, 8, 2, 7, 3, 10, 9, 1, 6, 5>

따라서 예제의 특수성에 빠지지 않도록 조심할 것!

https://www.acmicpc.net/problem/10828

 

 

 

 

 

 

 

 

※ 이번 문제에서 알게된 점

if (!s.compare("문자열"))

§ a.compare(b) 함수

C++에서 string 헤더에 제공되는 함수.

a와 b의 문자열이 서로 같다면 return 0
a와 b의 문자열이 서로 다르면 return -1

 

§ if (!s.compare("문자열")) 

문자열이 같으면 0을 반환하므로 if(0)은 if (false)와 동일하게 취급된다.
따라서 if (!s.compare("문자열")은 if (!0) 즉, if (!false) == if(true)이다.

 

 

 

 

§ my solution 

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

int main(){
    int n;  cin >> n;
    stack<int> intStack;
    for (int i = 0; i < n; i++) {
        string s;   cin >> s;
        if (!s.compare("push")){
            int push_num;   cin >> push_num;
            intStack.push(push_num);
        }

        else if (!s.compare("pop")){
            if (intStack.empty())
                cout << "-1" << endl;
            else{
                cout << intStack.top() << endl;
                intStack.pop();
            }
        }

        else if (!s.compare("size")){
            cout << intStack.size() << endl;
        }

        else if (!s.compare("empty")){
            if (intStack.empty())
                cout << "1" << endl;
            else
                cout << "0" << endl;
        }

        else if (!s.compare("top")){
            if (intStack.empty())
                cout << "-1" << endl;
            else
                cout << intStack.top() << endl;

        }
    }
}

 

+ Recent posts