Редакция для Расчёт зарплаты с переработками
Submitting an official solution before solving the problem yourself is a bannable offence.
Автор:
1. Идея
Нужно для каждого сотрудника посчитать оплату за каждый час его смены отдельно, а затем сложить.
Для каждого часа есть два независимых фактора:
- какой это по счёту час смены:
- с 1-го по 8-й — обычная ставка
r; - с 9-го по 12-й — ставка
r * 3 / 2; - с 13-го и дальше — ставка
2 * r;
- с 1-го по 8-й — обычная ставка
- является ли этот час ночным:
- если да, к оплате этого часа добавляется
20%, то есть умножение на6 / 5.
- если да, к оплате этого часа добавляется
Так как смена всегда состоит из целого числа часов, удобно просто пройти по всем часам смены циклом и для каждого часа вычислить его вклад в общую сумму.
2. Наблюдения
1. Как найти длительность смены
Если t2 > t1, то смена закончилась в те же сутки, и длительность равна t2 - t1.
Если t2 <= t1, значит смена перешла через полночь, и длительность равна t2 + 24 - t1.
Например:
10:00 -> 18:00даёт8часов;21:00 -> 06:00даёт9часов;22:00 -> 02:00даёт4часа.
2. Как определить ночной час
Час считается ночным, если он целиком лежит в интервале от 22:00 до 06:00.
Так как все моменты времени кратны часу, достаточно смотреть на час начала этого часового отрезка:
- если он от
22до23, то час ночной; - если он от
0до5, то тоже ночной.
То есть условие такое:
hour_start >= 22илиhour_start < 6.
3. Чтобы дроби считались точно, удобно работать в 50-х долях рубля
В условии есть множители 3 / 2 (полуторная ставка) и 6 / 5 (ночная надбавка). По условию дробная часть не должна теряться по ходу расчёта — она отбрасывается только в самом конце.
Чтобы не использовать вещественные числа и не терять копейки, эталонное решение считает всё в 1/50 рубля:
- обычная ставка
r— этоr * 50 / 50, то естьr * 50единиц; - полуторная ставка
r * 3 / 2— этоr * 75 / 50, то естьr * 75единиц (всегда целое); - двойная ставка
2 * r— этоr * 100 / 50, то естьr * 100единиц; - ночная надбавка — умножение на
6 / 5; так как все базовые значения кратны5, после умножения на6и деления на5снова получается целое число единиц.
После суммирования всех часов остаётся поделить итог на 50 (целочисленно), чтобы получить рубли с округлением вниз.
3. Алгоритм
Для каждого сотрудника:
- Считать
name,r,t1,t2. - Выделить из
t1иt2часыh1иh2. - Вычислить длительность смены:
- если
h2 > h1, тоduration = h2 - h1; - иначе
duration = h2 + 24 - h1.
- если
- Завести
total50 = 0— сумму в 50-х долях рубля. - Для каждого номера часа смены
kот1доduration:- Найти час начала этого отрезка:
hour_start = (h1 + k - 1) % 24.
- Определить, ночной ли он:
night = (hour_start >= 22 or hour_start < 6).
- Найти базовую оплату этого часа в 50-х долях:
- если
k <= 8, тоpay50 = r * 50; - иначе если
k <= 12, тоpay50 = r * 75; - иначе
pay50 = r * 100.
- если
- Если час ночной, умножить:
pay50 = pay50 * 6 / 5.
- Прибавить
pay50кtotal50.
- Найти час начала этого отрезка:
- Итоговая сумма в рублях:
pay = total50 / 50(целочисленно).
- Вывести
nameиpay.
4. Почему это работает
Разбиение смены на часы
По условию вся смена состоит из целого числа полных часов. Значит, её можно представить как последовательность часовых отрезков и для каждого по отдельности определить тариф (1–8 / 9–12 / 13+) и ночной или дневной он.
Корректное определение времени каждого часа
Если смена начинается в h1, то начало k-го часа равно (h1 + k - 1) % 24 — с учётом перехода через полночь.
Корректное определение ночных часов
Час ночной тогда и только тогда, когда его начало находится в 22, 23 или в 0..5. Это эквивалентно hour_start >= 22 || hour_start < 6.
Почему считать в 50-х долях достаточно
Все базовые ставки и ночная надбавка дают ровно один из множителей 1, 3/2, 2, 6/5. В 50-х долях:
r→50 * r;r * 3 / 2→75 * r(так как50 * 3 / 2 = 75);2 * r→100 * r;- умножение на
6 / 5— поскольку все значения выше кратны5, после*6 / 5снова получается целое.
Поэтому за всё время расчёта мы работаем с целыми числами и не теряем дробных частей. Деление в самом конце даёт ровно округление итоговой суммы вниз.
5. Сложность
Пусть у одного сотрудника длительность смены равна d. Тогда мы делаем d итераций цикла.
Так как по условию d <= 16, работа на одного сотрудника занимает O(d), то есть фактически O(1).
Для всех сотрудников:
- время:
O(n); - память:
O(1)без учёта входных данных.
6. Код на C++17
#include <iostream>
#include <string>
using namespace std;
int parse_hour(const string& s) {
return (s[0] - '0') * 10 + (s[1] - '0');
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
string name, t1, t2;
long long r;
cin >> name >> r >> t1 >> t2;
int h1 = parse_hour(t1);
int h2 = parse_hour(t2);
int duration;
if (h2 > h1) duration = h2 - h1;
else duration = h2 + 24 - h1;
long long total50 = 0;
for (int k = 1; k <= duration; k++) {
int hour_start = (h1 + k - 1) % 24;
bool night = (hour_start >= 22 || hour_start < 6);
long long pay50;
if (k <= 8) {
pay50 = r * 50;
} else if (k <= 12) {
pay50 = r * 75;
} else {
pay50 = r * 100;
}
if (night) {
pay50 = pay50 * 6 / 5;
}
total50 += pay50;
}
long long pay = total50 / 50;
cout << name << ' ' << pay << '\n';
}
return 0;
}
7. Код на Python 3
def parse_hour(s):
return int(s[:2])
n = int(input())
for _ in range(n):
name, r, t1, t2 = input().split()
r = int(r)
h1 = parse_hour(t1)
h2 = parse_hour(t2)
if h2 > h1:
duration = h2 - h1
else:
duration = h2 + 24 - h1
total50 = 0
for k in range(1, duration + 1):
hour_start = (h1 + k - 1) % 24
night = (hour_start >= 22 or hour_start < 6)
if k <= 8:
pay50 = r * 50
elif k <= 12:
pay50 = r * 75
else:
pay50 = r * 100
if night:
pay50 = pay50 * 6 // 5
total50 += pay50
pay = total50 // 50
print(name, pay)
Комментарии