정화 코딩

[ICPC Sinchon] 25W 알고리즘 캠프 초급 강의 2회차 과제 에디토리얼 본문

PS

[ICPC Sinchon] 25W 알고리즘 캠프 초급 강의 2회차 과제 에디토리얼

jungh150c 2025. 3. 5. 00:13

2회차 - 선형 자료구조, 제곱 정렬

 


</1406> 에디터

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

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

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

    string s;
    cin >> s;
    list<char> li(s.begin(), s.end());

    int n;
    cin >> n;

    // 최초의 커서 위치는 문장의 맨 뒤
    auto cur = li.end();

    while (n--) {
        char cmd;
        cin >> cmd;

        if (cmd == 'L') {
            // 커서가 맨 앞이 아니라면 한 칸 앞으로 이동
            if (cur != li.begin()) cur--;
        } else if (cmd == 'D') {
            // 커서가 맨 뒤가 아니라면 한 칸 뒤로 이동
            if (cur != li.end()) cur++;
        } else if (cmd == 'B') {
            // 커서가 맨 앞이 아니라면 바로 앞 문자 하나 삭제
            if (cur != li.begin()) li.erase(prev(cur));
        } else if (cmd == 'P') {
            // 커서 바로 앞에 문자 하나 추가
            char c;
            cin >> c;
            li.insert(cur, c);
        }
    }

    for (auto i = li.begin(); i != li.end(); i++) cout << *i;
    cout << '\n';
}

 


</9012> 괄호

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

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

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

    int t;
    cin >> t;

    while (t--) {
        string ans = "YES\n";
        stack<char> stk;
        string s;
        cin >> s;

        for (char c: s) {
            // 왼쪽 괄호면 스택에 넣기
            // 오른쪽 괄호면 스택에서 맨 위 하나를 꺼내서 확인
            //              꺼낼 수 없으면 (오른쪽 괄호가 남으면) "NO"
            if (c == '(') {
                stk.push(c);
            } else if (c == ')') {
                if (stk.empty()) {
                    ans = "NO\n";
                    break;
                } else {
                    stk.pop();
                }
            }
        }

        // 스택이 비어있지 않다면 (왼쪽 괄호가 남으면) "NO"
        if (!stk.empty()) ans = "NO\n";
        cout << ans;
    }
}

 


</24511> queuestack

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

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

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

    // n: 자료구조의 개수
    int n;
    cin >> n;

    // qn: 큐의 개수
    int qn = 0;
    // isQue: 특정 위치가 큐인지 저장하는 용도의 배열
    // (초기 원소 하나를 삽입할 때 사용됨.)
    vector<bool> isQue(n, false);

    for (int i = 0; i < n; i++) {
        int x;
        cin >> x;
        // x가 0, 즉 큐일 때만 체크하고 개수 카운트
        if (x == 0) {
            isQue[i] = true;
            qn++;
        }
    }

    // q: 큐의 배열
    vector<queue<int>> q(qn);
    // idx: 큐 배열의 인덱스
    int idx = 0;

    for (int i = 0; i < n; i++) {
        int x;
        cin >> x;
        // 큐일 때만 초기 원소 삽입
        if (isQue[i]) q[idx++].push(x);
    }

    // m: 삽입할 수열의 길이
    int m;
    cin >> m;

    while (m--) {
        int x;
        cin >> x;

        // x를 삽입하고 새로운 반환값을 x에 넣는 것을 반복
        for (int i = 0; i < qn; i++) {
            q[i].push(x);
            x = q[i].front();
            q[i].pop();
        }

        cout << x << ' ';
    }
    cout << '\n';
}

이렇게 풀면 시간 초과가 납니다…

시간을 더 줄이기 위해서는 여러 큐가 마치 하나의 큐로 동작한다는 것을 이해해야 합니다.

이는 큐의 FIFO 성질 때문입니다.

말로 이해가 잘 되지 않는다면 아래 그림을 참고해주세요.

 

수정된 코드는 아래와 같습니다.

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

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

    // n: 자료구조의 개수
    int n;
    cin >> n;

    // q: 큐
    queue<int> q;

    vector<int> a(n);
    vector<int> b(n);

    for (int i = 0; i < n; i++) cin >> a[i];
    for (int i = 0; i < n; i++) cin >> b[i];

    for (int i = n - 1; i >= 0; i--) {
        if (a[i] == 0) q.push(b[i]);
    }

    // m: 삽입할 수열의 길이
    int m;
    cin >> m;

    while (m--) {
        int x;
        cin >> x;

        q.push(x);
        cout << q.front() << ' ';
        q.pop();
    }
    cout << '\n';
}

 


</2493> 탑

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

현재 탑에서 발사하는 레이저가 신호를 수신하는 탑은 현재 탑에서 왼쪽으로 이동하면서 볼 때, 자신보다 높이가 높은 탑 중 처음 만나는 탑입니다.

좀 더 풀어서 설명하면, 우선 탑은 레이저 신호를 왼쪽으로 발사하기 때문에 현재 탑에서 시작하여 왼쪽으로 이동하면 살펴보아야 합니다. 현재 탑보다 낮은 탑들은 신호가 닿지 않기 때문에 무시하고, 현재 탑보다 높은 탑들 중 처음으로 만나는 탑이 현재 탑의 신호를 수신하게 됩니다.

이해를 돕기 위해 그림으로 그려보았습니다.

이 아이디어를 사용하여 스택을 관리하면 됩니다. 코드와 그에 대한 간단한 설명은 아래에 있습니다.

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

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

    int n;
    cin >> n;

    stack<pair<int, int>> s;
    for (int i = 1; i <= n; i++) {
        // x: 현재 탑의 높이
        int x;
        cin >> x;

        // 현재 탑 높이보다 낮은 것들 스택에서 전부 빼기
        while (!s.empty() && s.top().second < x) {
            s.pop();
        }

        // 현재 탑에서 발사한 레이저 신호를 수신하는 탑은
        // 현재 탑에서 왼쪽으로 이동하면서 볼 때, 자신보다 높이가 높은 탑 중 처음 만나는 탑
        if (s.empty()) cout << 0 << ' ';
        else cout << s.top().first << ' ';

        s.emplace(i, x);
    }

    cout << '\n';
}

 


</30885> Φ²

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

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

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

    list<pair<long long, int>> li;
    int n;
    cin >> n;
    for (int i = 1; i < n + 1; i++) {
        int x;
        cin >> x;
        li.emplace_back(x, i);
    }

    while (li.size() > 1) {
        for (auto i = li.begin(); i != li.end(); i++) {
            long long a = i->first; // 현재 가리키고 있는 미생물의 크기
            long long newa = a; // 새로운 미생물의 크기 (흡수 후)

            // 첫 번째 미생물이 아닐 때만
            if (i != li.begin()) {
                auto prev_i = prev(i);
                // 바로 앞 미생물이 작거나 같으면
                if (prev_i->first <= a) {
                    newa += prev_i->first;
                    // i는 계속 현재 미생물을 가리키도록 유지
                    i = li.erase(prev_i);
                }
            }

            // 마지막 미생물이 아닐 때만
            if (next(i) != li.end()) {
                auto next_i = next(i);
                // 바로 뒤 미생물이 작거나 같으면
                if (next_i->first <= a) {
                    newa += next_i->first;
                    // i는 계속 현재 미생물을 가리키도록 유지
                    i = li.erase(next_i);
                    i--;
                }
            }

            // 미생물의 크기 갱신
            i->first = newa;
        }
    }

    auto ans = li.begin();
    cout << ans->first << '\n' << ans->second << '\n';
}

우선 모든 미생물을 연결 리스트에 담습니다. first에는 미생물의 크기를, second에는 미생물의 초기 위치를 넣었습니다.

그 후 미생물이 한 마리만 남을 때까지 다음을 반복합니다.

 

1. 앞에 미생물이 존재할 경우, 바로 앞 미생물이 작거나 같으면 흡수합니다. 이 때, 바로 앞 미생물의 바로 다음 미생물이 현재 미생물이므로 i는 erase에서 그대로 받습니다.
2. 뒤에 미생물이 존재할 경우, 바로 뒤 미생물이 작거나 같으면 흡수합니다. 이 때, 바로 뒤 미생물의 바로 다음 미생물은 현재 미생물 기준 바로 뒤 미생물이 되므로 i는 erase에서 받은 후 1을 빼줍니다.
3. 미생물의 크기를 흡수 후의 크기로 갱신해줍니다.

 

- erase 함수는 list에서 해당 iterator 위치의 원소를 삭제하고 원래 위치의 다음 원소의 iterator를 반환합니다.
- prev 함수는 list에서 해당 iterator 위치의 원소의 바로 앞 원소의 iterator를 반환합니다.
- next 함수는 list에서 해당 iterator 위치의 원소의 바로 뒤 원소의 iterator를 반환합니다.
- prev, next 함수를 사용할 때는 범위가 벗어나 정의되지 않은 동작(Undefined Behavior)이 발생하지 않도록 주의해야 합니다.

 

Comments