USACO 2022년 12월 실버 대회 - Bronze Division 풀이

1. Cow College - 최대 수익 구하기

Farmer John이 소들을 위한 대학을 세우려고 한다. N마리의 소(1 ≤ N ≤ 10^5)가 있으며, 각 소는 최대 c_i(1 ≤ c_i ≤ 10^6)만큼의 학비를 낼 의향이 있다. 등록금을 책정했을 때, 소가 지불할 수 있는 최대 금액보다 높으면 해당 소는入学하지 않는다. FJ는 최대 수익을 얻고자 하며, 그때의 등록금을 구해야 한다. 여러 답이 있다면 가장 작은 등록금을 출력한다.

접근 방법: 소들이 지불 가능한 금액을 정렬한 후, 각 금액을 등록금으로 설정했을 때의 수익을 계산한다. 정렬된 배열에서 i번째 요소를 등록금으로 하면, (N - i + 1)마리의 소가 등록한다. 따라서 수익 = a[i] * (N - i + 1)이다. 최대 수익을 갱신하며 최소 등록금을 찾는다.

시간 복잡도는 O(N log N) (정렬 포함)이다.

#include <bits/stdc++.h>
using namespace std;

int main() {
    long long N;
    cin >> N;
    vector<long long> cows(N);
    for (int i = 0; i < N; ++i) cin >> cows[i];
    sort(cows.begin(), cows.end());

    long long max_profit = 0, best_tuition = 0;
    for (int i = 0; i < N; ++i) {
        long long profit = cows[i] * (N - i);
        if (profit > max_profit) {
            max_profit = profit;
            best_tuition = cows[i];
        }
    }
    cout << max_profit << " " << best_tuition << "\n";
    return 0;
}

2. Feeding the Cows - 목초지 최소 배치

N마리의 소가 일렬로 서 있고, 각 소는 G(건지) 또는 H(홀스타인) 품종이다. FJ는 1부터 N까지의 위치에 목초지를 심을 수 있으며, 한 위치에는 한 가지 품종만 먹일 수 있다. 각 소는 최대 K(0 ≤ K ≤ N-1) 거리 이내의 목초지로 이동 가능하다. 모든 소를 먹이기 위해 필요한 최소 목초지 개수와 배치 방법을 구한다.

접근 방법: 탐욕법을 사용한다. 각 소를 왼쪽에서 오른쪽으로 확인하며, 아직 먹이를 못 먹은 소가 있으면 그 위치에서 K만큼 떨어진 곳(i + K)에 해당 품종의 목초지를 심는다. 만약 i + K가 N을 초과하면, 빈 자리 중 가장 오른쪽에 심는다. 목초지를 심은 후에는 해당 품종의 소가 영향을 받는 범위(i + 2K)까지 건너뛴다.

#include <bits/stdc++.h>
using namespace std;

int main() {
    int T;
    cin >> T;
    while (T--) {
        int N, K;
        string s;
        cin >> N >> K >> s;
        vector<char> pasture(N, '.');
        int cnt = 0;
        int last_G = -1, last_H = -1;

        for (int i = 0; i < N; ++i) {
            if (s[i] == 'G' && i > last_G) {
                int pos = min(i + K, N - 1);
                while (pasture[pos] != '.') --pos;
                pasture[pos] = 'G';
                ++cnt;
                last_G = pos + K;
            } else if (s[i] == 'H' && i > last_H) {
                int pos = min(i + K, N - 1);
                while (pasture[pos] != '.') --pos;
                pasture[pos] = 'H';
                ++cnt;
                last_H = pos + K;
            }
        }
        cout << cnt << "\n";
        for (char c : pasture) cout << c;
        cout << "\n";
    }
    return 0;
}

3. Reverse Engineering - 프로그램 검증

Elsie가 N개의 0/1 입력을 받아 0 또는 1을 반환하는 if-else if-else 프로그램을 만들었다. 각 조건문은 하나의 입력 변수만 확인한다. Bessie는 M개의 입력-출력 쌍을 알고 있으며, 이와 일치하는 프로그램이 존재하는지 판단해야 한다. 존재하면 "OK", 없으면 "LIE"를 출력한다.

접근 방법: 가능한 변수와 값을 하나씩 제거하는 방식으로 검증한다. 현재 남아 있는 입력들 중에서, 특정 변수의 특정 값(0 또는 1)이 항상 같은 출력을 내는 조건이 있다면 그 분기를 확정하고 해당 조건을 만족하는 입력을 제거한다. 이 과정을 더 이상 분기를 찾을 수 없을 때까지 반복한다. 최종적으로 모든 입력이 제거되면 OK, 그렇지 않으면 LIE이다.

#include <bits/stdc++.h>
using namespace std;

int main() {
    int T;
    cin >> T;
    while (T--) {
        int N, M;
        cin >> N >> M;
        vector<string> inputs(M);
        vector<int> outputs(M);
        for (int i = 0; i < M; ++i) cin >> inputs[i] >> outputs[i];

        vector<bool> removed(M, false);
        vector<bool> used_var(N, false);
        bool changed = true;
        while (changed) {
            changed = false;
            for (int v = 0; v < N; ++v) {
                if (used_var[v]) continue;
                int count[2][2] = {{0,0},{0,0}}; // [value][output]
                for (int i = 0; i < M; ++i) {
                    if (removed[i]) continue;
                    int val = inputs[i][v] - '0';
                    int out = outputs[i];
                    count[val][out]++;
                }
                // check if this variable with a specific value always leads to same output
                if ((count[0][0] == 0 && count[0][1] > 0) || (count[0][1] == 0 && count[0][0] > 0)) {
                    changed = true;
                    used_var[v] = true;
                    for (int i = 0; i < M; ++i) {
                        if (!removed[i] && inputs[i][v] == '0') removed[i] = true;
                    }
                }
                if ((count[1][0] == 0 && count[1][1] > 0) || (count[1][1] == 0 && count[1][0] > 0)) {
                    changed = true;
                    used_var[v] = true;
                    for (int i = 0; i < M; ++i) {
                        if (!removed[i] && inputs[i][v] == '1') removed[i] = true;
                    }
                }
            }
        }
        bool all_removed = true;
        for (bool r : removed) if (!r) all_removed = false;
        cout << (all_removed ? "OK" : "LIE") << "\n";
    }
    return 0;
}

태그: USACO 알고리즘 그리디 정렬 시뮬레이션

7월 31일 14:39에 게시됨