PS

[백준(BOJ) 15460번] My Cow Ate My Homework 풀이

yonsweng 2020. 1. 23. 17:50

문제 : https://www.acmicpc.net/problem/15460

 

s[0...n-1]에 주어진 score를 저장합시다.

이 문제는 k를 1부터 n-2까지 증가시키며, s[k]...s[n-1] 중 최솟값을 제외한 것의 평균을 최대로 하는 k들을 구하는 문제입니다.

 

최대 n이 100,000이므로 O(n^2)이 되면 안되는데, 다음과 같이 O(n)에 score의 부분합부분최솟값을 미리 구해놓으면 나중에 s[k]...s[n-1] 중 최솟값을 제외한 것의 평균O(1)에 구할 수 있습니다.

 

a[i] : s[n-1]부터 s[i]까지 합.

m[i] : s[n-1]부터 s[i]까지 최솟값.

 

그 다음부터는 k를 1부터 n-2까지 증가시키며, a[k] - m[k] / (n - k - 1)이 최대가 되는 k들을 출력하면 됩니다.

 

그런데, a[k] - m[k] / (n - k - 1)은 실수 형태이기 때문에 여러 k가 같은 최댓값을 가지는 지 비교할 때 오차가 생길 수 있습니다. 우리는 이 값을 최대로 하는 a[k] - m[k]n - k - 1을 따로 저장하면서 비교하면 이 문제를 해결할 수 있습니다.

 

중간에 int 범위를 초과할 수 있는 연산이 있으므로 long long을 쓰면 됩니다.


#include <iostream>
#include <vector>

using namespace std;

int n;
int s[100000];
int a[100000];  // accumulated sum from back
int m[100000];  // min from back

int main() {
    ios::sync_with_stdio(false), cin.tie(NULL);

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

    a[n - 1] = m[n - 1] = s[n - 1];
    for (int i = n - 2; i >= 0; i--) {
        a[i] = a[i + 1] + s[i];
        m[i] = min(s[i], m[i + 1]);
    }

    pair<int, int> max_avg = {0, 1};  // {sum, cnt}
    for (int k = 1; k < n - 1; k++) {
        // a[k] - m[k] / (n - k - 1) > max_avg
        if ((long long) (a[k] - m[k]) * max_avg.second > (long long) max_avg.first * (n - k - 1))
            max_avg.first = a[k] - m[k], max_avg.second = n - k - 1;
    }

    for (int k = 1; k < n - 1; k++) {
        // a[k] - m[k] / (n - k - 1) == max_avg
        if ((long long) (a[k] - m[k]) * max_avg.second == (long long) max_avg.first * (n - k - 1))
            cout << k << '\n';
    }

    return 0;
}