Всего студенты разбились на 4 группы — те кто посетил только первый ресторан, кто только второй, кто посетил оба ресторана и кто не сдал экзамен. Один из наиболее простых вариантов отсечь все некорректные случаи — посчитать, сколько студентов оказалось в каждой группе. Для первой группы это значение равно A - C, для второй: B - C, для третьей: C, для четвертой: N - A - B + C. Теперь достаточно проверить, что в первых трех группах неотрицательное число студентов, а в последней — положительное. Если это так, то количество студентов в последней группе и есть ответ на задачу.
int n1 = a - c;
int n2 = b - c;
int n3 = c;
int n4 = n - n1 - n2 - n3;
if (n1 >= 0 && n2 >= 0 && n3 >= 0 && n4 > 0)
cout << n4;
else
cout << -1;
В общем случае, рекомендуется ознакомиться с методом включений-исключений.
Кроме того, ограничения в задаче позволяют перебрать значения количества студентов в каждой группе (кроме третьей) за O(N3) и проверить, соответствует ли такое разбиение условию. Если ни одно разбиение не подошло — вывести - 1:
int n3 = c;
for (int n1 = 0; n1 <= n; n1++)
for (int n2 = 0; n2 <= n; n2++)
for (int n4 = 1; n4 <= n; n4++)
if (n1 + n3 == a && n2 + n3 == b && n1 + n2 + n3 + n4 == n) {
cout << n4;
return 0;
}
cout << -1;
В этой задаче применим жадный алгоритм. Действительно, Васе выгодно сначала исправлять наиболее низкие оценки. Поэтому можно отсортировать полученные оценки в порядке возрастания и начать заменять их на 5, пока не получим желаемый результат. Для проверки результата можно после каждой операции вычислять среднее арифметическое заново (итого, сложность будет O(N2)) или поддерживать текущую сумму оценок, пересчитывая среднее арифметическое за O(1) с общей сложностью O(N), без учета сортировки, например, так:
bool check(int sum, int n) {
// integer representation of sum / n >= 4.5
return sum * 10 >= n * 45;
}
int main() {
... // read integers to vector v and calculate sum
sort(v.begin(), v.end());
int ans = 0;
while (!check(sum, n)) {
sum += 5 - v[ans];
ans++;
}
cout << ans;
}
Разумеется, оба подхода легко укладываются в ограничение по времени. Кроме того, рекомендуется избежать вычисление среднего арифметического значения с использованием вещественных чисел.
Несложно проверить, что для некоторого k условие выполняется (Вася съест не менее половины эклеров), то и для любого большего k это условие выполнится.
Действительно, рассмотрим количество эклеров, которое останется вечером каждого дня при некотором выбранном k1 — пусть в день i останется ai эклеров. Если мы возьмем для Васи другое число k2 > k1, то в первый вечер останется меньше эклеров: b1 < a1. Но тогда Петя вечером съест эклеров не больше, чем в первом случае и на следующий день их будет меньше. Аналогично с учетом k2 > k1 и к вечеру второго дня b2 < a2 и так далее. Таким образом, для любого i получаем bi < ai, а значит и Петя каждый день будет есть не больше эклеров, чем в первом случае. Значит, Петя в сумме съест не больше эклеров, чем в первом случае, а Вася — не меньше.
Значит, эту задачу можно решать при помощи бинарного поиска по ответу. Для проверки конкретного выбранного k можно использовать простую симуляцию процесса, описанного в условии.
bool check(long long k, long long n) {
long long sum = 0;
long long cur = n;
while (cur > 0) {
long long o = min(cur, k);
sum += o;
cur -= o;
cur -= cur / 10;
}
return sum * 2 >= n;
}
Поскольку Петя съедает 10% от количества оставшихся эклеров, то это количество убывает экспоненциально, значит, пройдет достаточно мало дней прежде чем будут съедены все эклеры. В худшем случае ребятам потребуется 378 дней. Формально же асимптотика этого решения есть O(log(n)2).
Как и в предыдущей задаче рекомендуется избежать работы с вещественными числами и все вычисления производить в целых типах.