Редакция для Расчёт зарплаты с переработками


Remember to use this editorial only when stuck, and not to copy-paste code from it. Please be respectful to the problem author and editorialist.
Submitting an official solution before solving the problem yourself is a bannable offence.

Автор: montes332

1. Идея

Нужно для каждого сотрудника посчитать оплату за каждый час его смены отдельно, а затем сложить.

Для каждого часа есть два независимых фактора:

  • какой это по счёту час смены:
    • с 1-го по 8-й — обычная ставка r;
    • с 9-го по 12-й — ставка r * 3 / 2;
    • с 13-го и дальше — ставка 2 * r;
  • является ли этот час ночным:
    • если да, к оплате этого часа добавляется 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. Алгоритм

Для каждого сотрудника:

  1. Считать name, r, t1, t2.
  2. Выделить из t1 и t2 часы h1 и h2.
  3. Вычислить длительность смены:
    • если h2 > h1, то duration = h2 - h1;
    • иначе duration = h2 + 24 - h1.
  4. Завести total50 = 0 — сумму в 50-х долях рубля.
  5. Для каждого номера часа смены k от 1 до duration:
    1. Найти час начала этого отрезка:
      • hour_start = (h1 + k - 1) % 24.
    2. Определить, ночной ли он:
      • night = (hour_start >= 22 or hour_start < 6).
    3. Найти базовую оплату этого часа в 50-х долях:
      • если k <= 8, то pay50 = r * 50;
      • иначе если k <= 12, то pay50 = r * 75;
      • иначе pay50 = r * 100.
    4. Если час ночной, умножить:
      • pay50 = pay50 * 6 / 5.
    5. Прибавить pay50 к total50.
  6. Итоговая сумма в рублях:
    • pay = total50 / 50 (целочисленно).
  7. Вывести 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-х долях:

  • r50 * r;
  • r * 3 / 275 * r (так как 50 * 3 / 2 = 75);
  • 2 * r100 * 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)

Комментарии

Еще нет ни одного комментария.